diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/CustomFishingHook.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/CustomFishingHook.java index 2af19a71..b5c19909 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/CustomFishingHook.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/CustomFishingHook.java @@ -48,10 +48,7 @@ import net.momirealms.customfishing.common.util.TriConsumer; import net.momirealms.customfishing.common.util.TriFunction; import net.momirealms.sparrow.heart.SparrowHeart; import net.momirealms.sparrow.heart.feature.inventory.HandSlot; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.Statistic; +import org.bukkit.*; import org.bukkit.entity.*; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; @@ -447,10 +444,11 @@ public class CustomFishingHook { public void handleSuccessfulFishing() { // update the hook location - context.arg(ContextKeys.OTHER_LOCATION, hook.getLocation()); - context.arg(ContextKeys.OTHER_X, hook.getLocation().getBlockX()); - context.arg(ContextKeys.OTHER_Y, hook.getLocation().getBlockY()); - context.arg(ContextKeys.OTHER_Z, hook.getLocation().getBlockZ()); + Location hookLocation = hook.getLocation(); + context.arg(ContextKeys.OTHER_LOCATION, hookLocation); + context.arg(ContextKeys.OTHER_X, hookLocation.getBlockX()); + context.arg(ContextKeys.OTHER_Y, hookLocation.getBlockY()); + context.arg(ContextKeys.OTHER_Z, hookLocation.getBlockZ()); LootType lootType = context.arg(ContextKeys.LOOT); Objects.requireNonNull(lootType, "Missing loot type"); @@ -505,7 +503,7 @@ public class CustomFishingHook { } } if (item != null) { - FishingLootSpawnEvent spawnEvent = new FishingLootSpawnEvent(context, hook.getLocation(), nextLoot, item); + FishingLootSpawnEvent spawnEvent = new FishingLootSpawnEvent(context, hookLocation, nextLoot, item); Bukkit.getPluginManager().callEvent(spawnEvent); if (!spawnEvent.summonEntity()) item.remove(); @@ -517,7 +515,7 @@ public class CustomFishingHook { } } doSuccessActions(); - }, (long) ConfigManager.multipleLootSpawnDelay() * i, hook.getLocation()); + }, (long) ConfigManager.multipleLootSpawnDelay() * i, hookLocation); } } case BLOCK -> { diff --git a/api/src/main/java/net/momirealms/customfishing/api/storage/DataStorageProvider.java b/api/src/main/java/net/momirealms/customfishing/api/storage/DataStorageProvider.java index 18d6f6a6..5e9f233b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/storage/DataStorageProvider.java +++ b/api/src/main/java/net/momirealms/customfishing/api/storage/DataStorageProvider.java @@ -26,6 +26,7 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; /** * Interface representing a provider for data storage. @@ -54,11 +55,12 @@ public interface DataStorageProvider { /** * Retrieves the player data for the specified UUID. * - * @param uuid the UUID of the player - * @param lock whether to lock the player data for exclusive access + * @param uuid the UUID of the player + * @param lock whether to lock the player data for exclusive access + * @param executor The executor, can be null * @return a {@link CompletableFuture} containing an {@link Optional} with the player data, or empty if not found */ - CompletableFuture> getPlayerData(UUID uuid, boolean lock); + CompletableFuture> getPlayerData(UUID uuid, boolean lock, Executor executor); /** * Updates the player data for the specified UUID. diff --git a/common/src/main/java/net/momirealms/customfishing/common/helper/VersionHelper.java b/common/src/main/java/net/momirealms/customfishing/common/helper/VersionHelper.java index ebbfac50..5deb63ae 100644 --- a/common/src/main/java/net/momirealms/customfishing/common/helper/VersionHelper.java +++ b/common/src/main/java/net/momirealms/customfishing/common/helper/VersionHelper.java @@ -102,6 +102,10 @@ public class VersionHelper { return version >= 20.0; } + public static boolean isVersionNewerThan1_20_2() { + return version >= 20.2; + } + public static boolean isVersionNewerThan1_20_5() { return version >= 20.5; } diff --git a/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CompetitionPapi.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CompetitionPapi.java index 944ffca9..3fa9951d 100644 --- a/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CompetitionPapi.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CompetitionPapi.java @@ -144,6 +144,6 @@ public class CompetitionPapi extends PlaceholderExpansion { return Optional.ofNullable(competition.getRanking().getPlayerAt(Integer.parseInt(split[1]))).orElse(""); } } - return "null"; + return null; } } diff --git a/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/StatisticsPapi.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/StatisticsPapi.java index dee0012c..65deb85d 100644 --- a/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/StatisticsPapi.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/StatisticsPapi.java @@ -17,9 +17,13 @@ package net.momirealms.customfishing.bukkit.integration.papi; +import io.lumine.mythic.bukkit.utils.caffeine.cache.Cache; +import io.lumine.mythic.bukkit.utils.caffeine.cache.Caffeine; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; import net.momirealms.customfishing.api.mechanic.statistic.FishingStatistics; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.api.storage.data.StatisticData; import net.momirealms.customfishing.api.storage.user.UserData; import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; @@ -27,13 +31,22 @@ import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; public class StatisticsPapi extends PlaceholderExpansion { + private final Cache> offlineDataCache; + private final BukkitCustomFishingPlugin plugin; public StatisticsPapi(BukkitCustomFishingPlugin plugin) { this.plugin = plugin; + this.offlineDataCache = Caffeine.newBuilder() + .expireAfterWrite(5, TimeUnit.MINUTES) + .build(); } public void load() { @@ -67,51 +80,112 @@ public class StatisticsPapi extends PlaceholderExpansion { @Override public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) { Optional onlineUser = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); - return onlineUser.map( - data -> { - FishingStatistics statistics = data.statistics(); - String[] split = params.split("_", 2); - switch (split[0]) { - case "total" -> { - return String.valueOf(statistics.amountOfFishCaught()); - } - case "hascaught" -> { - if (split.length == 1) return "Invalid format"; - return String.valueOf(statistics.getAmount(split[1]) != 0); - } - case "amount" -> { - if (split.length == 1) return "Invalid format"; - return String.valueOf(statistics.getAmount(split[1])); - } - case "size-record" -> { - float size = statistics.getMaxSize(split[1]); - return String.format("%.2f", size < 0 ? 0 : size); - } - case "category" -> { - if (split.length == 1) return "Invalid format"; - String[] categorySplit = split[1].split("_", 2); - if (categorySplit.length == 1) return "Invalid format"; - List category = plugin.getStatisticsManager().getCategoryMembers(categorySplit[1]); - if (categorySplit[0].equals("total")) { - int total = 0; - for (String loot : category) { - total += statistics.getAmount(loot); + String[] split = params.split("_", 2); + if (onlineUser.isPresent()) { + return onlineUser.map( + data -> { + FishingStatistics statistics = data.statistics(); + switch (split[0]) { + case "total" -> { + return String.valueOf(statistics.amountOfFishCaught()); + } + case "hascaught" -> { + if (split.length == 1) return "Invalid format"; + return String.valueOf(statistics.getAmount(split[1]) != 0); + } + case "amount" -> { + if (split.length == 1) return "Invalid format"; + return String.valueOf(statistics.getAmount(split[1])); + } + case "size-record" -> { + float size = statistics.getMaxSize(split[1]); + return String.format("%.2f", size < 0 ? 0 : size); + } + case "category" -> { + if (split.length == 1) return "Invalid format"; + String[] categorySplit = split[1].split("_", 2); + if (categorySplit.length == 1) return "Invalid format"; + List category = plugin.getStatisticsManager().getCategoryMembers(categorySplit[1]); + if (categorySplit[0].equals("total")) { + int total = 0; + for (String loot : category) { + total += statistics.getAmount(loot); + } + return String.valueOf(total); + } else if (categorySplit[0].equals("progress")) { + int size = category.size(); + int unlocked = 0; + for (String loot : category) { + if (statistics.getAmount(loot) != 0) unlocked++; + } + double percent = ((double) unlocked * 100) / size; + String progress = String.format("%.1f", percent); + return progress.equals("100.0") ? "100" : progress; } - return String.valueOf(total); - } else if (categorySplit[0].equals("progress")) { - int size = category.size(); - int unlocked = 0; - for (String loot : category) { - if (statistics.getAmount(loot) != 0) unlocked++; - } - double percent = ((double) unlocked * 100) / size; - String progress = String.format("%.1f", percent); - return progress.equals("100.0") ? "100" : progress; } } + return null; } - return ""; + ).orElse(""); + } else { + Optional optional = offlineDataCache.get(player.getUniqueId(), (uuid) -> { + CompletableFuture> data = plugin.getStorageManager().getDataSource().getPlayerData(player.getUniqueId(), false, Runnable::run); + try { + return data.get(); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException(e); } - ).orElse("Data not loaded"); + }); + if (optional.isPresent()) { + PlayerData playerData = optional.get(); + StatisticData statistics = playerData.statistics(); + switch (split[0]) { + case "total" -> { + int total = 0; + for (int i : statistics.amountMap.values()) { + total += i; + } + return String.valueOf(total); + } + case "hascaught" -> { + if (split.length == 1) return "Invalid format"; + return String.valueOf(statistics.amountMap.getOrDefault(split[1], 0) != 0); + } + case "amount" -> { + if (split.length == 1) return "Invalid format"; + return String.valueOf(statistics.amountMap.getOrDefault(split[1], 0)); + } + case "size-record" -> { + float size = statistics.sizeMap.getOrDefault(split[1], 0f); + return String.format("%.2f", size < 0 ? 0 : size); + } + case "category" -> { + if (split.length == 1) return "Invalid format"; + String[] categorySplit = split[1].split("_", 2); + if (categorySplit.length == 1) return "Invalid format"; + List category = plugin.getStatisticsManager().getCategoryMembers(categorySplit[1]); + if (categorySplit[0].equals("total")) { + int total = 0; + for (String loot : category) { + total += statistics.amountMap.getOrDefault(loot, 0); + } + return String.valueOf(total); + } else if (categorySplit[0].equals("progress")) { + int size = category.size(); + int unlocked = 0; + for (String loot : category) { + if (statistics.amountMap.getOrDefault(loot, 0) != 0) unlocked++; + } + double percent = ((double) unlocked * 100) / size; + String progress = String.format("%.1f", percent); + return progress.equals("100.0") ? "100" : progress; + } + } + } + return null; + } else { + return ""; + } + } } } diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/block/BukkitBlockManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/block/BukkitBlockManager.java index f4cca18c..4e462787 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/block/BukkitBlockManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/block/BukkitBlockManager.java @@ -26,6 +26,7 @@ import net.momirealms.customfishing.api.mechanic.config.ConfigManager; import net.momirealms.customfishing.api.mechanic.context.Context; import net.momirealms.customfishing.api.mechanic.context.ContextKeys; import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import net.momirealms.customfishing.common.helper.VersionHelper; import net.momirealms.customfishing.common.util.RandomUtils; import net.momirealms.customfishing.common.util.Tuple; import org.bukkit.*; @@ -249,8 +250,13 @@ public class BukkitBlockManager implements BlockManager, Listener { } Location hookLocation = requireNonNull(context.arg(ContextKeys.OTHER_LOCATION)); Location playerLocation = requireNonNull(context.holder()).getLocation(); - FallingBlock fallingBlock = hookLocation.getWorld().spawn(hookLocation, FallingBlock.class); - fallingBlock.setBlockData(blockData); + FallingBlock fallingBlock; + if (VersionHelper.isVersionNewerThan1_20_2()) { + fallingBlock = hookLocation.getWorld().spawn(hookLocation, FallingBlock.class); + fallingBlock.setBlockData(blockData); + } else { + fallingBlock = hookLocation.getWorld().spawnFallingBlock(hookLocation, blockData); + } fallingBlock.getPersistentDataContainer().set( requireNonNull(NamespacedKey.fromString("block", plugin.getBootstrap())), PersistentDataType.STRING, diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ExportDataCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ExportDataCommand.java index da47d77e..ac7122e3 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ExportDataCommand.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ExportDataCommand.java @@ -77,7 +77,7 @@ public class ExportDataCommand extends BukkitCommandFeature { int amount = uuids.size(); for (UUID uuid : uuids) { - futures.add(storageProvider.getPlayerData(uuid, false).thenAccept(it -> { + futures.add(storageProvider.getPlayerData(uuid, false, null).thenAccept(it -> { if (it.isPresent()) { out.put(uuid, plugin.getStorageManager().toJson(it.get())); userCount.incrementAndGet(); diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java index d60e9a0a..1aa463f3 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java @@ -32,6 +32,7 @@ import dev.dejvokep.boostedyaml.utils.format.NodeRole; import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; import net.momirealms.customfishing.api.mechanic.MechanicType; import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.action.ActionManager; import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; import net.momirealms.customfishing.api.mechanic.block.BlockDataModifier; import net.momirealms.customfishing.api.mechanic.block.BlockDataModifierFactory; @@ -597,105 +598,157 @@ public class BukkitConfigManager extends ConfigManager { if (!section.contains("type")) { throw new RuntimeException(section.getRouteAsString()); } + Action[] actions = plugin.getActionManager().parseActions(section.getSection("actions")); switch (section.getString("type")) { case "lava-fishing" -> { return (((effect, context, phase) -> { - if (phase == 0) effect.properties().put(EffectProperties.LAVA_FISHING, true); + if (phase == 0) { + effect.properties().put(EffectProperties.LAVA_FISHING, true); + ActionManager.trigger(context, actions); + } })); } case "void-fishing" -> { return (((effect, context, phase) -> { - if (phase == 0) effect.properties().put(EffectProperties.VOID_FISHING, true); + if (phase == 0) { + effect.properties().put(EffectProperties.VOID_FISHING, true); + ActionManager.trigger(context, actions); + } })); } case "weight-mod" -> { var op = parseWeightOperation(section.getStringList("value")); return (((effect, context, phase) -> { - if (phase == 1) effect.weightOperations(op); + if (phase == 1) { + effect.weightOperations(op); + ActionManager.trigger(context, actions); + } })); } case "weight-mod-ignore-conditions" -> { var op = parseWeightOperation(section.getStringList("value")); return (((effect, context, phase) -> { - if (phase == 1) effect.weightOperationsIgnored(op); + if (phase == 1) { + effect.weightOperationsIgnored(op); + ActionManager.trigger(context, actions); + } })); } case "group-mod" -> { var op = parseGroupWeightOperation(section.getStringList("value")); return (((effect, context, phase) -> { - if (phase == 1) effect.weightOperations(op); + if (phase == 1) { + effect.weightOperations(op); + ActionManager.trigger(context, actions); + } })); } case "group-mod-ignore-conditions" -> { var op = parseGroupWeightOperation(section.getStringList("value")); return (((effect, context, phase) -> { - if (phase == 1) effect.weightOperationsIgnored(op); + if (phase == 1) { + effect.weightOperationsIgnored(op); + ActionManager.trigger(context, actions); + } })); } case "wait-time" -> { MathValue value = MathValue.auto(section.get("value")); return (((effect, context, phase) -> { - if (phase == 2) effect.waitTimeAdder(effect.waitTimeAdder() + value.evaluate(context)); + if (phase == 2) { + effect.waitTimeAdder(effect.waitTimeAdder() + value.evaluate(context)); + ActionManager.trigger(context, actions); + } })); } case "hook-time", "wait-time-multiplier" -> { MathValue value = MathValue.auto(section.get("value")); return (((effect, context, phase) -> { - if (phase == 2) effect.waitTimeMultiplier(effect.waitTimeMultiplier() - 1 + value.evaluate(context)); + if (phase == 2) { + effect.waitTimeMultiplier(effect.waitTimeMultiplier() - 1 + value.evaluate(context)); + ActionManager.trigger(context, actions); + } })); } case "difficulty" -> { MathValue value = MathValue.auto(section.get("value")); return (((effect, context, phase) -> { - if (phase == 2) effect.difficultyAdder(effect.difficultyAdder() + value.evaluate(context)); + if (phase == 2) { + effect.difficultyAdder(effect.difficultyAdder() + value.evaluate(context)); + ActionManager.trigger(context, actions); + } })); } case "difficulty-multiplier", "difficulty-bonus" -> { MathValue value = MathValue.auto(section.get("value")); return (((effect, context, phase) -> { - if (phase == 2) effect.difficultyMultiplier(effect.difficultyMultiplier() - 1 + value.evaluate(context)); + if (phase == 2) { + effect.difficultyMultiplier(effect.difficultyMultiplier() - 1 + value.evaluate(context)); + ActionManager.trigger(context, actions); + } })); } case "size" -> { MathValue value = MathValue.auto(section.get("value")); return (((effect, context, phase) -> { - if (phase == 2) effect.sizeAdder(effect.sizeAdder() + value.evaluate(context)); + if (phase == 2) { + effect.sizeAdder(effect.sizeAdder() + value.evaluate(context)); + ActionManager.trigger(context, actions); + } })); } case "size-multiplier", "size-bonus" -> { MathValue value = MathValue.auto(section.get("value")); return (((effect, context, phase) -> { - if (phase == 2) effect.sizeMultiplier(effect.sizeMultiplier() - 1 + value.evaluate(context)); + if (phase == 2) { + effect.sizeMultiplier(effect.sizeMultiplier() - 1 + value.evaluate(context)); + ActionManager.trigger(context, actions); + } })); } case "game-time" -> { MathValue value = MathValue.auto(section.get("value")); return (((effect, context, phase) -> { - if (phase == 2) effect.gameTimeAdder(effect.gameTimeAdder() + value.evaluate(context)); + if (phase == 2) { + effect.gameTimeAdder(effect.gameTimeAdder() + value.evaluate(context)); + ActionManager.trigger(context, actions); + } })); } case "game-time-multiplier", "game-time-bonus" -> { MathValue value = MathValue.auto(section.get("value")); return (((effect, context, phase) -> { - if (phase == 2) effect.gameTimeMultiplier(effect.gameTimeMultiplier() - 1 + value.evaluate(context)); + if (phase == 2) { + effect.gameTimeMultiplier(effect.gameTimeMultiplier() - 1 + value.evaluate(context)); + ActionManager.trigger(context, actions); + } })); } case "score" -> { MathValue value = MathValue.auto(section.get("value")); return (((effect, context, phase) -> { - if (phase == 2) effect.scoreAdder(effect.scoreAdder() + value.evaluate(context)); + if (phase == 2) { + effect.scoreAdder(effect.scoreAdder() + value.evaluate(context)); + ActionManager.trigger(context, actions); + } })); } case "score-multiplier", "score-bonus" -> { MathValue value = MathValue.auto(section.get("value")); return (((effect, context, phase) -> { - if (phase == 2) effect.scoreMultiplier(effect.scoreMultiplier() - 1 + value.evaluate(context)); + if (phase == 2) { + effect.scoreMultiplier(effect.scoreMultiplier() - 1 + value.evaluate(context)); + ActionManager.trigger(context, actions); + } })); } case "multiple-loot" -> { MathValue value = MathValue.auto(section.get("value")); return (((effect, context, phase) -> { - if (phase == 2) effect.multipleLootChance(effect.multipleLootChance() + value.evaluate(context)); + if (phase == 2) { + effect.multipleLootChance(effect.multipleLootChance() + value.evaluate(context)); + ActionManager.trigger(context, actions); + } })); } case "conditional" -> { diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemManager.java index 1e4c8f02..0b244bad 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemManager.java @@ -64,7 +64,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import static java.util.Objects.requireNonNull; @@ -211,10 +210,10 @@ public class BukkitItemManager implements ItemManager, Listener { itemEntity.setInvulnerable(true); // prevent from being killed by lava - plugin.getScheduler().asyncLater(() -> { + plugin.getScheduler().sync().runLater(() -> { if (itemEntity.isValid()) itemEntity.setInvulnerable(false); - }, 1, TimeUnit.SECONDS); + }, 20, hookLocation); itemEntity.setVelocity(vector); diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/BukkitStorageManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/BukkitStorageManager.java index ddff57ec..e6877395 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/BukkitStorageManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/BukkitStorageManager.java @@ -174,7 +174,7 @@ public class BukkitStorageManager implements StorageManager, Listener { @Override public CompletableFuture> getOfflineUserData(UUID uuid, boolean lock) { - CompletableFuture> optionalDataFuture = dataSource.getPlayerData(uuid, lock); + CompletableFuture> optionalDataFuture = dataSource.getPlayerData(uuid, lock, null); return optionalDataFuture.thenCompose(optionalUser -> { if (optionalUser.isEmpty()) { return CompletableFuture.completedFuture(Optional.empty()); @@ -280,7 +280,7 @@ public class BukkitStorageManager implements StorageManager, Listener { task.cancel(); return; } - redisManager.getPlayerData(uuid, false).thenAccept(optionalData -> { + redisManager.getPlayerData(uuid, false, null).thenAccept(optionalData -> { if (optionalData.isPresent()) { addOnlineUser(player, optionalData.get()); task.cancel(); @@ -305,7 +305,7 @@ public class BukkitStorageManager implements StorageManager, Listener { plugin.getPluginLogger().warn("Tried 3 times getting data for " + uuid + ". Giving up."); return; } - this.dataSource.getPlayerData(uuid, ConfigManager.lockData()).thenAccept(optionalData -> { + this.dataSource.getPlayerData(uuid, ConfigManager.lockData(), null).thenAccept(optionalData -> { // Data should not be empty if (optionalData.isEmpty()) { plugin.getPluginLogger().severe("Unexpected error: Data is null"); diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/MongoDBProvider.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/MongoDBProvider.java index ea270dbe..e8152002 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/MongoDBProvider.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/MongoDBProvider.java @@ -37,6 +37,7 @@ import org.bukkit.Bukkit; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; public class MongoDBProvider extends AbstractStorage { @@ -114,9 +115,10 @@ public class MongoDBProvider extends AbstractStorage { } @Override - public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { + public CompletableFuture> getPlayerData(UUID uuid, boolean lock, Executor executor) { var future = new CompletableFuture>(); - plugin.getScheduler().async().execute(() -> { + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { MongoCollection collection = database.getCollection(getCollectionName("data")); Document doc = collection.find(Filters.eq("uuid", uuid)).first(); if (doc == null) { diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/RedisManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/RedisManager.java index d48b88f9..488d96f8 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/RedisManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/RedisManager.java @@ -37,6 +37,7 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; public class RedisManager extends AbstractStorage { @@ -271,25 +272,29 @@ public class RedisManager extends AbstractStorage { /** * Asynchronously retrieve player data from Redis. * - * @param uuid The UUID of the player. - * @param lock Flag indicating whether to lock the data. + * @param uuid The UUID of the player. + * @param lock Flag indicating whether to lock the data. + * @param executor The executor, can be null * @return A CompletableFuture with an optional PlayerData. */ @Override - public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { + public CompletableFuture> getPlayerData(UUID uuid, boolean lock, Executor executor) { var future = new CompletableFuture>(); - try (Jedis jedis = jedisPool.getResource()) { - byte[] key = getRedisKey("cf_data", uuid); - byte[] data = jedis.get(key); - jedis.del(key); - if (data != null) { - future.complete(Optional.of(plugin.getStorageManager().fromBytes(data))); - } else { + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + try (Jedis jedis = jedisPool.getResource()) { + byte[] key = getRedisKey("cf_data", uuid); + byte[] data = jedis.get(key); + jedis.del(key); + if (data != null) { + future.complete(Optional.of(plugin.getStorageManager().fromBytes(data))); + } else { + future.complete(Optional.empty()); + } + } catch (Exception e) { future.complete(Optional.empty()); } - } catch (Exception e) { - future.complete(Optional.empty()); - } + }); return future; } diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/AbstractSQLDatabase.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/AbstractSQLDatabase.java index 255a4f12..f706fb58 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/AbstractSQLDatabase.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/AbstractSQLDatabase.java @@ -32,6 +32,7 @@ import java.nio.charset.StandardCharsets; import java.sql.*; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; /** * An abstract base class for SQL database implementations that handle player data storage. @@ -115,46 +116,47 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { @SuppressWarnings("DuplicatedCode") @Override - public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { + public CompletableFuture> getPlayerData(UUID uuid, boolean lock, Executor executor) { var future = new CompletableFuture>(); - plugin.getScheduler().async().execute(() -> { - try ( - Connection connection = getConnection(); - PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data"))) - ) { - statement.setString(1, uuid.toString()); - ResultSet rs = statement.executeQuery(); - if (rs.next()) { - final Blob blob = rs.getBlob("data"); - final byte[] dataByteArray = blob.getBytes(1, (int) blob.length()); - blob.free(); - PlayerData data = plugin.getStorageManager().fromBytes(dataByteArray); - data.uuid(uuid); - if (lock) { - int lockValue = rs.getInt(2); - if (lockValue != 0 && getCurrentSeconds() - ConfigManager.dataSaveInterval() <= lockValue) { - connection.close(); - data.locked(true); - future.complete(Optional.of(data)); - plugin.getPluginLogger().warn("Player " + uuid + "'s data is locked. Retrying..."); - return; + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + try ( + Connection connection = getConnection(); + PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data"))) + ) { + statement.setString(1, uuid.toString()); + ResultSet rs = statement.executeQuery(); + if (rs.next()) { + final Blob blob = rs.getBlob("data"); + final byte[] dataByteArray = blob.getBytes(1, (int) blob.length()); + blob.free(); + PlayerData data = plugin.getStorageManager().fromBytes(dataByteArray); + data.uuid(uuid); + if (lock) { + int lockValue = rs.getInt(2); + if (lockValue != 0 && getCurrentSeconds() - ConfigManager.dataSaveInterval() <= lockValue) { + connection.close(); + data.locked(true); + future.complete(Optional.of(data)); + plugin.getPluginLogger().warn("Player " + uuid + "'s data is locked. Retrying..."); + return; + } } + if (lock) lockOrUnlockPlayerData(uuid, true); + future.complete(Optional.of(data)); + } else if (Bukkit.getPlayer(uuid) != null) { + // the player is online + var data = PlayerData.empty(); + data.uuid(uuid); + insertPlayerData(uuid, data, lock, connection); + future.complete(Optional.of(data)); + } else { + future.complete(Optional.empty()); } - if (lock) lockOrUnlockPlayerData(uuid, true); - future.complete(Optional.of(data)); - } else if (Bukkit.getPlayer(uuid) != null) { - // the player is online - var data = PlayerData.empty(); - data.uuid(uuid); - insertPlayerData(uuid, data, lock, connection); - future.complete(Optional.of(data)); - } else { - future.complete(Optional.empty()); + } catch (SQLException e) { + plugin.getPluginLogger().warn("Failed to get " + uuid + "'s data.", e); + future.completeExceptionally(e); } - } catch (SQLException e) { - plugin.getPluginLogger().warn("Failed to get " + uuid + "'s data.", e); - future.completeExceptionally(e); - } }); return future; } diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/SQLiteProvider.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/SQLiteProvider.java index da25c049..a572747c 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/SQLiteProvider.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/SQLiteProvider.java @@ -36,6 +36,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -113,8 +114,9 @@ public class SQLiteProvider extends AbstractSQLDatabase { @SuppressWarnings("DuplicatedCode") @Override - public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { + public CompletableFuture> getPlayerData(UUID uuid, boolean lock, Executor executor) { var future = new CompletableFuture>(); + if (executor == null) executor = this.executor; executor.execute(() -> { try ( Connection connection = getConnection(); diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/JsonProvider.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/JsonProvider.java index 21dfe59c..8ab5e109 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/JsonProvider.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/JsonProvider.java @@ -34,6 +34,7 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; /** * A data storage implementation that uses JSON files to store player data. @@ -53,7 +54,7 @@ public class JsonProvider extends AbstractStorage { } @Override - public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { + public CompletableFuture> getPlayerData(UUID uuid, boolean lock, Executor executor) { File file = getPlayerDataFile(uuid); PlayerData playerData; if (file.exists()) { diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/YAMLProvider.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/YAMLProvider.java index 385056c9..b345acb4 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/YAMLProvider.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/YAMLProvider.java @@ -34,6 +34,7 @@ import java.io.File; import java.io.IOException; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; public class YAMLProvider extends AbstractStorage { @@ -60,7 +61,7 @@ public class YAMLProvider extends AbstractStorage { } @Override - public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { + public CompletableFuture> getPlayerData(UUID uuid, boolean lock, Executor executor) { File dataFile = getPlayerDataFile(uuid); if (!dataFile.exists()) { if (Bukkit.getPlayer(uuid) != null) { diff --git a/gradle.properties b/gradle.properties index 3167db81..8c19384e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=2.2.26.1 +project_version=2.2.27 config_version=36 project_group=net.momirealms