From e503eac3f9d9200c07ee387dfe9459594c9a6a91 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Thu, 21 Sep 2023 04:43:58 +0800 Subject: [PATCH] comments --- .../api/data/DataStorageInterface.java | 2 +- .../api/manager/ActionManager.java | 87 +++++++- .../customfishing/api/manager/BagManager.java | 13 ++ .../api/manager/BlockManager.java | 71 ++++++- .../api/manager/CompetitionManager.java | 62 +++++- .../api/manager/EffectManager.java | 86 +++++++- .../api/manager/EntityManager.java | 24 ++- .../api/manager/FishingManager.java | 78 ++++++- .../api/manager/GameManager.java | 35 ++- .../api/manager/HookManager.java | 56 ++++- .../api/manager/IntegrationManager.java | 63 +++++- .../api/manager/ItemManager.java | 164 ++++++++++++-- .../api/manager/LootManager.java | 57 +++++ .../api/manager/MarketManager.java | 72 ++++++- .../api/manager/PlaceholderManager.java | 55 ++++- .../api/manager/RequirementManager.java | 76 +++++-- .../api/manager/StatisticsManager.java | 15 +- .../api/manager/StorageManager.java | 91 ++++++-- .../api/manager/TotemManager.java | 7 + .../competition/AbstractCompetitionInfo.java | 26 ++- .../competition/CompetitionConfig.java | 28 ++- .../competition/FishingCompetition.java | 83 +++++++- .../condition/FishingPreparation.java | 18 +- .../api/mechanic/game/BasicGameConfig.java | 12 ++ .../api/mechanic/game/GamingPlayer.java | 28 ++- .../CustomFishingPluginImpl.java | 46 +++- .../command/sub/CompetitionCommand.java | 2 +- .../command/sub/DebugCommand.java | 2 +- .../compatibility/IntegrationManagerImpl.java | 83 +++++++- .../item/CustomFishingItemImpl.java | 2 +- .../papi/PlaceholderManagerImpl.java | 67 +++++- .../mechanic/action/ActionManagerImpl.java | 201 ++++++++++++++---- .../mechanic/action/EmptyAction.java | 17 ++ .../mechanic/bag/BagManagerImpl.java | 48 ++++- .../mechanic/block/BlockManagerImpl.java | 137 ++++++++++-- .../mechanic/competition/Competition.java | 105 ++++++++- .../competition/CompetitionManagerImpl.java | 99 ++++++--- .../mechanic/effect/EffectManagerImpl.java | 109 ++++++++-- .../mechanic/entity/EntityManagerImpl.java | 28 ++- .../mechanic/fishing/BaitAnimationTask.java | 2 +- .../mechanic/fishing/FishingManagerImpl.java | 174 +++++++++------ .../mechanic/fishing/HookCheckTimerTask.java | 2 +- .../mechanic/game/GameManagerImpl.java | 67 +++++- .../mechanic/hook/HookManagerImpl.java | 129 ++++++++++- .../mechanic/item/ItemManagerImpl.java | 195 ++++++++++++++--- .../mechanic/loot/LootManagerImpl.java | 130 +++++++++-- .../mechanic/market/MarketGUI.java | 61 ++++++ .../mechanic/market/MarketGUIElement.java | 6 +- .../mechanic/market/MarketManagerImpl.java | 126 ++++++++++- .../mechanic/misc/CoolDownManager.java | 28 ++- .../requirement/ConditionalElement.java | 21 +- .../requirement/EmptyRequirement.java | 3 + .../requirement/RequirementManagerImpl.java | 170 +++++++++++---- .../statistic/StatisticsManagerImpl.java | 13 ++ .../mechanic/totem/ActivatedTotem.java | 2 +- .../mechanic/totem/TotemManagerImpl.java | 10 +- .../mechanic/totem/block/type/EqualType.java | 2 +- .../scheduler/BukkitSchedulerImpl.java | 30 +++ .../scheduler/FoliaSchedulerImpl.java | 29 +++ .../scheduler/SchedulerImpl.java | 66 ++++++ .../scheduler/SyncScheduler.java | 27 ++- .../storage/StorageManagerImpl.java | 153 +++++++++++-- .../storage/method/AbstractStorage.java | 24 ++- .../method/database/nosql/MongoDBImpl.java | 63 +++++- .../method/database/nosql/RedisManager.java | 105 ++++++--- .../database/sql/AbstractHikariDatabase.java | 21 ++ .../database/sql/AbstractSQLDatabase.java | 91 +++++++- .../storage/method/database/sql/H2Impl.java | 9 + .../method/database/sql/SQLiteImpl.java | 49 ++++- .../storage/method/file/JsonImpl.java | 31 +++ .../storage/method/file/YAMLImpl.java | 18 +- .../storage/user/OfflineUserImpl.java | 16 +- .../storage/user/OnlineUserImpl.java | 9 + .../customfishing/util/ArmorStandUtils.java | 78 ++++++- .../customfishing/util/ClassUtils.java | 10 + .../util/CompletableFutures.java | 24 ++- .../customfishing/util/ConfigUtils.java | 49 ++++- .../customfishing/util/DynamicText.java | 5 +- .../customfishing/util/FakeItemUtils.java | 52 ++++- .../customfishing/util/ItemUtils.java | 144 +++++++++++-- .../customfishing/util/LocationUtils.java | 7 + .../customfishing/util/NBTUtils.java | 32 +++ .../version/VersionManagerImpl.java | 19 +- plugin/src/main/resources/config.yml | 6 +- .../main/resources/contents/totem/default.yml | 6 +- 85 files changed, 3991 insertions(+), 578 deletions(-) create mode 100644 plugin/src/main/java/net/momirealms/customfishing/mechanic/action/EmptyAction.java diff --git a/api/src/main/java/net/momirealms/customfishing/api/data/DataStorageInterface.java b/api/src/main/java/net/momirealms/customfishing/api/data/DataStorageInterface.java index 003c178c..23f20510 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/data/DataStorageInterface.java +++ b/api/src/main/java/net/momirealms/customfishing/api/data/DataStorageInterface.java @@ -40,7 +40,7 @@ public interface DataStorageInterface { void updateManyPlayersData(Collection users, boolean unlock); - void lockPlayerData(UUID uuid, boolean lock); + void lockOrUnlockPlayerData(UUID uuid, boolean lock); Set getUniqueUsers(boolean legacy); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java index 1f03df63..b4050037 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java @@ -20,21 +20,106 @@ package net.momirealms.customfishing.api.manager; import net.momirealms.customfishing.api.mechanic.action.Action; import net.momirealms.customfishing.api.mechanic.action.ActionFactory; import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; +import net.momirealms.customfishing.api.mechanic.condition.Condition; import org.bukkit.configuration.ConfigurationSection; import java.util.HashMap; +import java.util.List; public interface ActionManager { + /** + * Registers an ActionFactory for a specific action type. + * This method allows you to associate an ActionFactory with a custom action type. + * + * @param type The custom action type to register. + * @param actionFactory The ActionFactory responsible for creating actions of the specified type. + * @return True if the registration was successful (the action type was not already registered), false otherwise. + */ boolean registerAction(String type, ActionFactory actionFactory); + /** + * Unregisters an ActionFactory for a specific action type. + * This method allows you to remove the association between an action type and its ActionFactory. + * + * @param type The custom action type to unregister. + * @return True if the action type was successfully unregistered, false if it was not found. + */ boolean unregisterAction(String type); + /** + * Retrieves an Action object based on the configuration provided in a ConfigurationSection. + * This method reads the type of action from the section, obtains the corresponding ActionFactory, + * and builds an Action object using the specified values and chance. + *

+ * events: + * success: + * action_1: <- section + * ... + * + * @param section The ConfigurationSection containing the action configuration. + * @return An Action object created based on the configuration, or an EmptyAction instance if the action type is invalid. + */ Action getAction(ConfigurationSection section); + /** + * Retrieves a mapping of ActionTriggers to arrays of Actions from a ConfigurationSection. + * This method iterates through the provided ConfigurationSection to extract action triggers + * and their associated arrays of Actions. + *

+ * events: <- section + * success: + * action_1: + * ... + * + * @param section The ConfigurationSection containing action mappings. + * @return A HashMap where keys are ActionTriggers and values are arrays of Action objects. + */ HashMap getActionMap(ConfigurationSection section); + /** + * Retrieves an array of Action objects from a ConfigurationSection. + * This method iterates through the provided ConfigurationSection to extract Action configurations + * and build an array of Action objects. + *

+ * events: + * success: <- section + * action_1: + * ... + * + * @param section The ConfigurationSection containing action configurations. + * @return An array of Action objects created based on the configurations in the section. + */ Action[] getActions(ConfigurationSection section); - ActionFactory getActionBuilder(String type); + /** + * Retrieves an ActionFactory associated with a specific action type. + * + * @param type The action type for which to retrieve the ActionFactory. + * @return The ActionFactory associated with the specified action type, or null if not found. + */ + ActionFactory getActionFactory(String type); + + /** + * Retrieves a mapping of success times to corresponding arrays of actions from a ConfigurationSection. + *

+ * events: + * success-times: <- section + * 1: + * action_1: + * ... + * + * @param section The ConfigurationSection containing success times actions. + * @return A HashMap where success times associated with actions. + */ + HashMap getTimesActionMap(ConfigurationSection section); + + /** + * Triggers a list of actions with the given condition. + * If the list of actions is not null, each action in the list is triggered. + * + * @param actions The list of actions to trigger. + * @param condition The condition associated with the actions. + */ + void triggerActions(List actions, Condition condition); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/BagManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/BagManager.java index 96d1d9b7..0efa7c0b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/BagManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/BagManager.java @@ -24,9 +24,22 @@ import org.bukkit.inventory.Inventory; import java.util.UUID; public interface BagManager { + boolean isEnabled(); + /** + * Retrieves the online bag inventory associated with a player's UUID. + * + * @param uuid The UUID of the player for whom the bag inventory is retrieved. + * @return The online bag inventory if the player is online, or null if not found. + */ Inventory getOnlineBagInventory(UUID uuid); + /** + * Initiates the process of editing the bag inventory of an offline player by an admin. + * + * @param admin The admin player performing the edit. + * @param userData The OfflineUser data of the player whose bag is being edited. + */ void editOfflinePlayerBag(Player admin, OfflineUser userData); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/BlockManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/BlockManager.java index 8492ffaf..baa69366 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/BlockManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/BlockManager.java @@ -24,19 +24,82 @@ import net.momirealms.customfishing.api.mechanic.loot.Loot; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; public interface BlockManager { - boolean registerBlockLibrary(BlockLibrary library); - boolean unregisterBlockLibrary(BlockLibrary library); + /** + * Registers a BlockLibrary instance. + * This method associates a BlockLibrary with its unique identification and adds it to the registry. + * + * @param blockLibrary The BlockLibrary instance to register. + * @return True if the registration was successful (the identification is not already registered), false otherwise. + */ + boolean registerBlockLibrary(BlockLibrary blockLibrary); - boolean unregisterBlockLibrary(String library); + /** + * Unregisters a BlockLibrary instance by its identification. + * This method removes a BlockLibrary from the registry based on its unique identification. + * + * @param identification The unique identification of the BlockLibrary to unregister. + * @return True if the BlockLibrary was successfully unregistered, false if it was not found. + */ + boolean unregisterBlockLibrary(String identification); + /** + * Registers a BlockDataModifierBuilder for a specific type. + * This method associates a BlockDataModifierBuilder with its type and adds it to the registry. + * + * @param type The type of the BlockDataModifierBuilder to register. + * @param builder The BlockDataModifierBuilder instance to register. + * @return True if the registration was successful (the type is not already registered), false otherwise. + */ boolean registerBlockDataModifierBuilder(String type, BlockDataModifierBuilder builder); + /** + * Registers a BlockStateModifierBuilder for a specific type. + * This method associates a BlockStateModifierBuilder with its type and adds it to the registry. + * + * @param type The type of the BlockStateModifierBuilder to register. + * @param builder The BlockStateModifierBuilder instance to register. + * @return True if the registration was successful (the type is not already registered), false otherwise. + */ boolean registerBlockStateModifierBuilder(String type, BlockStateModifierBuilder builder); + /** + * Unregisters a BlockDataModifierBuilder with the specified type. + * + * @param type The type of the BlockDataModifierBuilder to unregister. + * @return True if the BlockDataModifierBuilder was successfully unregistered, false otherwise. + */ + boolean unregisterBlockDataModifierBuilder(String type); + + /** + * Unregisters a BlockStateModifierBuilder with the specified type. + * + * @param type The type of the BlockStateModifierBuilder to unregister. + * @return True if the BlockStateModifierBuilder was successfully unregistered, false otherwise. + */ + boolean unregisterBlockStateModifierBuilder(String type); + + /** + * Summons a falling block at a specified location based on the provided loot. + * This method spawns a falling block at the given hookLocation with specific properties determined by the loot. + * + * @param player The player who triggered the action. + * @param hookLocation The location where the hook is positioned. + * @param playerLocation The location of the player. + * @param loot The loot to be associated with the summoned block. + */ void summonBlock(Player player, Location hookLocation, Location playerLocation, Loot loot); - String getAnyBlockID(Block block); + /** + * Retrieves the block ID associated with a given Block instance using block detection order. + * This method iterates through the configured block detection order to find the block's ID + * by checking different BlockLibrary instances in the specified order. + * + * @param block The Block instance for which to retrieve the block ID. + * @return The block ID + */ + @NotNull String getAnyPluginBlockID(Block block); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/CompetitionManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/CompetitionManager.java index 862b315a..51c282ff 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/CompetitionManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/CompetitionManager.java @@ -20,27 +20,67 @@ package net.momirealms.customfishing.api.manager; import net.momirealms.customfishing.api.mechanic.competition.CompetitionConfig; import net.momirealms.customfishing.api.mechanic.competition.CompetitionGoal; import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Set; -import java.util.concurrent.CompletableFuture; public interface CompetitionManager { - Set getAllCompetitions(); - String getCompetitionLocale(CompetitionGoal goal); + /** + * Retrieves a set of all competition names. + * + * @return A set of competition names. + */ + @NotNull Set getAllCompetitionKeys(); - void startCompetition(String competition, boolean force, boolean allServers); + /** + * Retrieves the localization key for a given competition goal. + * + * @param goal The competition goal to retrieve the localization key for. + * @return The localization key for the specified competition goal. + */ + @NotNull String getCompetitionGoalLocale(CompetitionGoal goal); - @Nullable - FishingCompetition getOnGoingCompetition(); + /** + * Starts a competition with the specified name, allowing for the option to force start it or apply it to the entire server. + * + * @param competition The name of the competition to start. + * @param force Whether to force start the competition even if amount of the online players is lower than the requirement + * @param allServers Whether to apply the competition to the servers that connected to Redis. + * @return {@code true} if the competition was started successfully, {@code false} otherwise. + */ + boolean startCompetition(String competition, boolean force, boolean allServers); - void startCompetition(CompetitionConfig config, boolean force, boolean allServers); + /** + * Gets the ongoing fishing competition, if one is currently in progress. + * + * @return The ongoing fishing competition, or null if there is none. + */ + @Nullable FishingCompetition getOnGoingCompetition(); + /** + * Starts a competition using the specified configuration. + * + * @param config The configuration of the competition to start. + * @param force Whether to force start the competition even if amount of the online players is lower than the requirement + * @param allServers Whether the competition should start across all servers that connected to Redis + * @return True if the competition was started successfully, false otherwise. + */ + boolean startCompetition(CompetitionConfig config, boolean force, boolean allServers); + + /** + * Gets the number of seconds until the next competition. + * + * @return The number of seconds until the next competition. + */ int getNextCompetitionSeconds(); - CompletableFuture getPlayerCount(); - - @Nullable - CompetitionConfig getConfig(String key); + /** + * Retrieves the configuration for a competition based on its key. + * + * @param key The key of the competition configuration to retrieve. + * @return The {@link CompetitionConfig} for the specified key, or {@code null} if no configuration exists with that key. + */ + @Nullable CompetitionConfig getConfig(String key); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/EffectManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/EffectManager.java index 5850f179..7637040b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/EffectManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/EffectManager.java @@ -19,16 +19,94 @@ package net.momirealms.customfishing.api.manager; import net.momirealms.customfishing.api.common.Key; import net.momirealms.customfishing.api.mechanic.effect.EffectCarrier; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; import net.momirealms.customfishing.api.mechanic.effect.FishingEffect; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public interface EffectManager { - boolean registerEffectItem(Key key, EffectCarrier effect); + /** + * Registers an EffectCarrier with a unique Key. + * + * @param key The unique Key associated with the EffectCarrier. + * @param effect The EffectCarrier to be registered. + * @return True if the registration was successful, false if the Key already exists. + */ + boolean registerEffectCarrier(Key key, EffectCarrier effect); - boolean unregisterEffectItem(Key key); + /** + * Unregisters an EffectCarrier associated with the specified Key. + * + * @param key The unique Key of the EffectCarrier to unregister. + * @return True if the EffectCarrier was successfully unregistered, false if the Key does not exist. + */ + boolean unregisterEffectCarrier(Key key); - @Nullable EffectCarrier getEffect(String namespace, String id); + /** + * Checks if an EffectCarrier with the specified namespace and id exists. + * + * @param namespace The namespace of the EffectCarrier. + * @param id The unique identifier of the EffectCarrier. + * @return True if an EffectCarrier with the given namespace and id exists, false otherwise. + */ + boolean hasEffectCarrier(String namespace, String id); - FishingEffect getInitialEffect(); + /** + * Retrieves an EffectCarrier with the specified namespace and id. + * + * @param namespace The namespace of the EffectCarrier. + * @param id The unique identifier of the EffectCarrier. + * @return The EffectCarrier with the given namespace and id, or null if it doesn't exist. + */ + @Nullable EffectCarrier getEffectCarrier(String namespace, String id); + + /** + * Parses a ConfigurationSection to create an EffectCarrier based on the specified key and configuration. + *

+ * xxx_item: <- section + * effects: + * ... + * events: + * ... + * + * @param key The key that uniquely identifies the EffectCarrier. + * @param section The ConfigurationSection containing the EffectCarrier configuration. + * @return An EffectCarrier instance based on the key and configuration, or null if the section is null. + */ + EffectCarrier getEffectCarrierFromSection(Key key, ConfigurationSection section); + + /** + * Retrieves the initial FishingEffect that represents no special effects. + * + * @return The initial FishingEffect. + */ + @NotNull FishingEffect getInitialEffect(); + + /** + * Parses a ConfigurationSection to retrieve an array of EffectModifiers. + *

+ * effects: <- section + * effect_1: + * type: xxx + * value: xxx + * + * @param section The ConfigurationSection to parse. + * @return An array of EffectModifiers based on the values found in the section. + */ + @NotNull EffectModifier[] getEffectModifiers(ConfigurationSection section); + + /** + * Parses a ConfigurationSection to create an EffectModifier based on the specified type and configuration. + *

+ * effects: + * effect_1: <- section + * type: xxx + * value: xxx + * + * @param section The ConfigurationSection containing the effect modifier configuration. + * @return An EffectModifier instance based on the type and configuration. + */ + @Nullable EffectModifier getEffectModifier(ConfigurationSection section); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/EntityManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/EntityManager.java index 06c120d4..25de6e97 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/EntityManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/EntityManager.java @@ -22,11 +22,29 @@ import net.momirealms.customfishing.api.mechanic.loot.Loot; import org.bukkit.Location; public interface EntityManager { + + /** + * Registers an entity library for use in the plugin. + * + * @param entityLibrary The entity library to register. + * @return {@code true} if the entity library was successfully registered, {@code false} if it already exists. + */ boolean registerEntityLibrary(EntityLibrary entityLibrary); - boolean unregisterEntityLibrary(String lib); - - boolean unregisterEntityLibrary(EntityLibrary entityLibrary); + /** + * Unregisters an entity library by its identification key. + * + * @param identification The identification key of the entity library to unregister. + * @return {@code true} if the entity library was successfully unregistered, {@code false} if it does not exist. + */ + boolean unregisterEntityLibrary(String identification); + /** + * Summons an entity based on the given loot configuration to a specified location. + * + * @param hookLocation The location where the entity will be summoned, typically where the fishing hook is. + * @param playerLocation The location of the player who triggered the entity summoning. + * @param loot The loot configuration that defines the entity to be summoned. + */ void summonEntity(Location hookLocation, Location playerLocation, Loot loot); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java index c29df9d5..d7cbdaaa 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java @@ -27,6 +27,7 @@ import net.momirealms.customfishing.api.mechanic.loot.Loot; import org.bukkit.entity.FishHook; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Map; @@ -34,27 +35,84 @@ import java.util.Optional; import java.util.UUID; public interface FishingManager { + + /** + * Removes a fishing hook entity associated with a given player's UUID. + * + * @param uuid The UUID of the player + * @return {@code true} if the fishing hook was successfully removed, {@code false} otherwise. + */ boolean removeHook(UUID uuid); + /** + * Retrieves a FishHook object associated with the provided player's UUID + * + * @param uuid The UUID of the player + * @return fishhook entity, null if not exists + */ + @Nullable FishHook getHook(UUID uuid); + + /** + * Sets the temporary fishing state for a player. + * + * @param player The player for whom to set the temporary fishing state. + * @param tempFishingState The temporary fishing state to set for the player. + */ void setTempFishingState(Player player, TempFishingState tempFishingState); - void removeHookCheckTask(Player player); + /** + * Gets the {@link TempFishingState} object associated with the given UUID. + * + * @param uuid The UUID of the player. + * @return The {@link TempFishingState} object if found, or {@code null} if not found. + */ + @Nullable TempFishingState getTempFishingState(UUID uuid); - Optional getHook(UUID uuid); - - void removeTempFishingState(Player player); + /** + * Removes the temporary fishing state associated with a player. + * + * @param player The player whose temporary fishing state should be removed. + */ + @Nullable TempFishingState removeTempFishingState(Player player); + /** + * Processes the game result for a gaming player + * + * @param gamingPlayer The gaming player whose game result should be processed. + */ void processGameResult(GamingPlayer gamingPlayer); - Collection getPossibleLootKeys(Condition condition); - - @NotNull Map getPossibleLootKeysWithWeight(Effect initialEffect, Condition condition); - - Loot getNextLoot(Effect initialEffect, Condition condition); - + /** + * Starts a fishing game for the specified player with the given condition and effect. + * + * @param player The player starting the fishing game. + * @param condition The condition used to determine the game. + * @param effect The effect applied to the game. + */ void startFishingGame(Player player, Condition condition, Effect effect); + /** + * Starts a fishing game for the specified player with the given settings and game instance. + * + * @param player The player starting the fishing game. + * @param settings The game settings for the fishing game. + * @param gameInstance The instance of the fishing game to start. + */ void startFishingGame(Player player, GameSettings settings, GameInstance gameInstance); + /** + * Checks if a player with the given UUID has cast their fishing hook. + * + * @param uuid The UUID of the player to check. + * @return {@code true} if the player has cast their fishing hook, {@code false} otherwise. + */ boolean hasPlayerCastHook(UUID uuid); + + /** + * Gets the {@link GamingPlayer} object associated with the given UUID. + * + * @param uuid The UUID of the player. + * @return The {@link GamingPlayer} object if found, or {@code null} if not found. + */ + @Nullable GamingPlayer getGamingPlayer(UUID uuid); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java index b515b54c..4811f7a9 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java @@ -29,14 +29,45 @@ import java.util.Optional; public interface GameManager { - + /** + * Registers a new game type with the specified type identifier. + * + * @param type The type identifier for the game. + * @param gameFactory The {@link GameFactory} that creates instances of the game. + * @return {@code true} if the registration was successful, {@code false} if the type identifier is already registered. + */ boolean registerGameType(String type, GameFactory gameFactory); + /** + * Unregisters a game type with the specified type identifier. + * + * @param type The type identifier of the game to unregister. + * @return {@code true} if the game type was successfully unregistered, {@code false} if the type identifier was not found. + */ boolean unregisterGameType(String type); + /** + * Retrieves the game factory associated with the specified game type. + * + * @param type The type identifier of the game. + * @return The {@code GameFactory} for the specified game type, or {@code null} if not found. + */ @Nullable GameFactory getGameFactory(String type); - Optional> getGame(String key); + /** + * Retrieves a game instance and its basic configuration associated with the specified key. + * + * @param key The key identifying the game instance. + * @return An {@code Optional} containing a {@code Pair} of the basic game configuration and the game instance + * if found, or an empty {@code Optional} if not found. + */ + @Nullable Pair getGameInstance(String key); + /** + * Retrieves a map of game names and their associated weights based on the specified conditions. + * + * @param condition The condition to evaluate game weights. + * @return A {@code HashMap} containing game names as keys and their associated weights as values. + */ HashMap getGameWithWeight(Condition condition); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/HookManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/HookManager.java index 040473a8..d0649926 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/HookManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/HookManager.java @@ -18,9 +18,61 @@ package net.momirealms.customfishing.api.manager; import net.momirealms.customfishing.api.mechanic.hook.HookSetting; +import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; public interface HookManager { - @Nullable - HookSetting getHookSetting(String id); + + /** + * Get the hook setting by its ID. + * + * @param id The ID of the hook setting to retrieve. + * @return The hook setting with the given ID, or null if not found. + */ + @Nullable HookSetting getHookSetting(String id); + + /** + * Decreases the durability of a fishing hook by a specified amount and optionally updates its lore. + * The hook would be removed if its durability is lower than 0 + * + * @param rod The fishing rod ItemStack to modify. + * @param amount The amount by which to decrease the durability. + * @param updateLore Whether to update the lore of the fishing rod. + */ + void decreaseHookDurability(ItemStack rod, int amount, boolean updateLore); + + /** + * Increases the durability of a hook by a specified amount and optionally updates its lore. + * + * @param rod The fishing rod ItemStack to modify. + * @param amount The amount by which to increase the durability. + * @param updateLore Whether to update the lore of the fishing rod. + */ + void increaseHookDurability(ItemStack rod, int amount, boolean updateLore); + + /** + * Sets the durability of a fishing hook to a specific amount and optionally updates its lore. + * + * @param rod The fishing rod ItemStack to modify. + * @param amount The new durability value to set. + * @param updateLore Whether to update the lore of the fishing rod. + */ + void setHookDurability(ItemStack rod, int amount, boolean updateLore); + + /** + * Equips a fishing hook on a fishing rod. + * + * @param rod The fishing rod ItemStack. + * @param hook The fishing hook ItemStack. + * @return True if the hook was successfully equipped, false otherwise. + */ + boolean equipHookOnRod(ItemStack rod, ItemStack hook); + + /** + * Removes the fishing hook from a fishing rod. + * + * @param rod The fishing rod ItemStack. + * @return The removed fishing hook ItemStack, or null if no hook was found. + */ + @Nullable ItemStack removeHookFromRod(ItemStack rod); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/IntegrationManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/IntegrationManager.java index 6e142faa..c78a91e9 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/IntegrationManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/IntegrationManager.java @@ -21,24 +21,81 @@ import net.momirealms.customfishing.api.integration.EnchantmentInterface; import net.momirealms.customfishing.api.integration.LevelInterface; import net.momirealms.customfishing.api.integration.SeasonInterface; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; import java.util.List; public interface IntegrationManager { + /** + * Registers a level plugin with the specified name. + * + * @param plugin The name of the level plugin. + * @param level The implementation of the LevelInterface. + * @return true if the registration was successful, false if the plugin name is already registered. + */ boolean registerLevelPlugin(String plugin, LevelInterface level); + /** + * Unregisters a level plugin with the specified name. + * + * @param plugin The name of the level plugin to unregister. + * @return true if the unregistration was successful, false if the plugin name is not found. + */ boolean unregisterLevelPlugin(String plugin); + /** + * Registers an enchantment provided by a plugin. + * + * @param plugin The name of the plugin providing the enchantment. + * @param enchantment The enchantment to register. + * @return true if the registration was successful, false if the enchantment name is already in use. + */ boolean registerEnchantment(String plugin, EnchantmentInterface enchantment); + /** + * Unregisters an enchantment provided by a plugin. + * + * @param plugin The name of the plugin providing the enchantment. + * @return true if the enchantment was successfully unregistered, false if the enchantment was not found. + */ boolean unregisterEnchantment(String plugin); - LevelInterface getLevelHook(String plugin); + /** + * Get the LevelInterface provided by a plugin. + * + * @param plugin The name of the plugin providing the LevelInterface. + * @return The LevelInterface provided by the specified plugin, or null if the plugin is not registered. + */ + @Nullable LevelInterface getLevelPlugin(String plugin); - List getEnchantments(ItemStack rod); + /** + * Get an enchantment plugin by its plugin name. + * + * @param plugin The name of the enchantment plugin. + * @return The enchantment plugin interface, or null if not found. + */ + @Nullable EnchantmentInterface getEnchantmentPlugin(String plugin); - SeasonInterface getSeasonInterface(); + /** + * Get a list of enchantment keys with level applied to the given ItemStack. + * + * @param itemStack The ItemStack to check for enchantments. + * @return A list of enchantment names applied to the ItemStack. + */ + List getEnchantments(ItemStack itemStack); + /** + * Get the current season interface, if available. + * + * @return The current season interface, or null if not available. + */ + @Nullable SeasonInterface getSeasonInterface(); + + /** + * Set the current season interface. + * + * @param season The season interface to set. + */ void setSeasonInterface(SeasonInterface season); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/ItemManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/ItemManager.java index 16f37bb1..e718f2b3 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/ItemManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/ItemManager.java @@ -21,7 +21,6 @@ import net.momirealms.customfishing.api.common.Key; import net.momirealms.customfishing.api.mechanic.item.BuildableItem; import net.momirealms.customfishing.api.mechanic.item.ItemBuilder; import net.momirealms.customfishing.api.mechanic.item.ItemLibrary; -import net.momirealms.customfishing.api.mechanic.loot.Loot; import org.bukkit.Location; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; @@ -34,45 +33,174 @@ import java.util.Set; public interface ItemManager { + /** + * Build an ItemStack with a specified namespace and value for a player. + * + * @param player The player for whom the ItemStack is being built. + * @param namespace The namespace of the item. + * @param value The value of the item. + * @return The constructed ItemStack. + */ @Nullable ItemStack build(Player player, String namespace, String value); + /** + * Build an ItemStack with a specified namespace and value, replacing placeholders, + * for a player. + * + * @param player The player for whom the ItemStack is being built. + * @param namespace The namespace of the item. + * @param value The value of the item. + * @param placeholders The placeholders to replace in the item's attributes. + * @return The constructed ItemStack, or null if the item doesn't exist. + */ @Nullable ItemStack build(Player player, String namespace, String value, Map placeholders); - @NotNull - ItemStack build(Player player, ItemBuilder builder); + /** + * Build an ItemStack using an ItemBuilder for a player. + * + * @param player The player for whom the ItemStack is being built. + * @param builder The ItemBuilder used to construct the ItemStack. + * @return The constructed ItemStack. + */ + @NotNull ItemStack build(Player player, ItemBuilder builder); - ItemStack buildAnyItemByID(Player player, String id); + /** + * Build an ItemStack using the provided ItemBuilder, player, and placeholders. + * + * @param player The player for whom the item is being built. + * @param builder The ItemBuilder that defines the item's properties. + * @param placeholders A map of placeholders and their corresponding values to be applied to the item. + * @return The constructed ItemStack. + */ + @NotNull ItemStack build(Player player, ItemBuilder builder, Map placeholders); - @Nullable - String getItemID(ItemStack itemStack); + /** + * Build an ItemStack for a player based on the provided item ID. + * + * @param player The player for whom the ItemStack is being built. + * @param id The item ID, which include an identification (e.g., "Oraxen:id"). + * @return The constructed ItemStack or null if the ID is not valid. + */ + @Nullable ItemStack buildAnyPluginItemByID(Player player, String id); - String getAnyItemID(ItemStack itemStack); + /** + * Get the item ID associated with the given ItemStack, if available. + * + * @param itemStack The ItemStack to retrieve the item ID from. + * @return The item ID without type or null if not found or if the ItemStack is null or empty. + */ + @Nullable String getCustomFishingItemID(ItemStack itemStack); - @Nullable - ItemBuilder getItemBuilder(ConfigurationSection section, String type, String id); + /** + * Get the item ID associated with the given ItemStack by checking all available item libraries. + * The detection order is determined by the configuration. + * + * @param itemStack The ItemStack to retrieve the item ID from. + * @return The item ID or "AIR" if not found or if the ItemStack is null or empty. + */ + @NotNull String getAnyPluginItemID(ItemStack itemStack); - ItemStack build(Player player, ItemBuilder builder, Map placeholders); + /** + * Create a ItemBuilder instance for an item configuration section + *

+ * xxx_item: <- section + * material: xxx + * custom-model-data: xxx + * + * @param section The configuration section containing item settings. + * @param type The type of the item (e.g., "rod", "bait"). + * @param id The unique identifier for the item. + * @return A CFBuilder instance representing the configured item, or null if the section is null. + */ + @Nullable ItemBuilder getItemBuilder(ConfigurationSection section, String type, String id); + /** + * Get a set of all item keys in the CustomFishing plugin. + * + * @return A set of item keys. + */ Set getAllItemsKey(); - boolean registerCustomItem(String namespace, String value, BuildableItem buildableItem); - - boolean unregisterCustomItem(String namespace, String value); - + /** + * Retrieve a BuildableItem by its namespace and value. + * + * @param namespace The namespace of the BuildableItem. + * @param value The value of the BuildableItem. + * @return The BuildableItem with the specified namespace and value, or null if not found. + */ @Nullable BuildableItem getBuildableItem(String namespace, String value); + /** + * Register an item library. + * + * @param itemLibrary The item library to register. + * @return True if the item library was successfully registered, false if it already exists. + */ boolean registerItemLibrary(ItemLibrary itemLibrary); - boolean unRegisterItemLibrary(ItemLibrary itemLibrary); + /** + * Unregister an item library. + * + * @param identification The item library to unregister. + * @return True if the item library was successfully unregistered, false if it doesn't exist. + */ + boolean unRegisterItemLibrary(String identification); - boolean unRegisterItemLibrary(String itemLibrary); - - void dropItem(Player player, Location hookLocation, Location playerLocation, Loot loot, Map args); + /** + * Drops an item based on the provided loot, applying velocity from a hook location to a player location. + * + * @param player The player for whom the item is intended. + * @param hookLocation The location where the item will initially drop. + * @param playerLocation The target location towards which the item's velocity is applied. + * @param id The loot object representing the item to be dropped. + * @param args A map of placeholders for item customization. + */ + void dropItem(Player player, Location hookLocation, Location playerLocation, String id, Map args); + /** + * Drops an item entity at the specified location and applies velocity towards another location. + * + * @param hookLocation The location where the item will initially drop. + * @param playerLocation The target location towards which the item's velocity is applied. + * @param itemStack The item stack to be dropped as an entity. + */ void dropItem(Location hookLocation, Location playerLocation, ItemStack itemStack); + /** + * Checks if the provided ItemStack is a custom fishing item + * + * @param itemStack The ItemStack to check. + * @return True if the ItemStack is a custom fishing item; otherwise, false. + */ boolean isCustomFishingItem(ItemStack itemStack); + + /** + * Decreases the durability of an ItemStack by a specified amount and optionally updates its lore. + * + * @param itemStack The ItemStack to modify. + * @param amount The amount by which to decrease the durability. + * @param updateLore Whether to update the lore of the ItemStack. + */ + void decreaseDurability(ItemStack itemStack, int amount, boolean updateLore); + + /** + * Increases the durability of an ItemStack by a specified amount and optionally updates its lore. + * + * @param itemStack The ItemStack to modify. + * @param amount The amount by which to increase the durability. + * @param updateLore Whether to update the lore of the ItemStack. + */ + void increaseDurability(ItemStack itemStack, int amount, boolean updateLore); + + /** + * Sets the durability of an ItemStack to a specific amount and optionally updates its lore. + * + * @param itemStack The ItemStack to modify. + * @param amount The new durability value. + * @param updateLore Whether to update the lore of the ItemStack. + */ + void setDurability(ItemStack itemStack, int amount, boolean updateLore); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/LootManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/LootManager.java index 2c7de1e6..9e7a98c1 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/LootManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/LootManager.java @@ -18,22 +18,79 @@ package net.momirealms.customfishing.api.manager; import net.momirealms.customfishing.api.mechanic.condition.Condition; +import net.momirealms.customfishing.api.mechanic.effect.Effect; import net.momirealms.customfishing.api.mechanic.loot.Loot; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Map; public interface LootManager { + /** + * Retrieves a list of loot IDs associated with a loot group key. + * + * @param key The key of the loot group. + * @return A list of loot IDs belonging to the specified loot group, or null if not found. + */ @Nullable List getLootGroup(String key); + /** + * Retrieves a loot configuration based on a provided loot key. + * + * @param key The key of the loot configuration. + * @return The Loot object associated with the specified loot key, or null if not found. + */ @Nullable Loot getLoot(String key); + /** + * Retrieves a collection of all loot configuration keys. + * + * @return A collection of all loot configuration keys. + */ Collection getAllLootKeys(); + /** + * Retrieves a collection of all loot configurations. + * + * @return A collection of all loot configurations. + */ Collection getAllLoots(); + /** + * Retrieves loot configurations with weights based on a given condition. + * + * @param condition The condition used to filter loot configurations. + * @return A mapping of loot configuration keys to their associated weights. + */ HashMap getLootWithWeight(Condition condition); + + /** + * Get a collection of possible loot keys based on a given condition. + * + * @param condition The condition to determine possible loot. + * @return A collection of loot keys. + */ + Collection getPossibleLootKeys(Condition condition); + + /** + * Get a map of possible loot keys with their corresponding weights, considering fishing effect and condition. + * + * @param initialEffect The effect to apply weight modifiers. + * @param condition The condition to determine possible loot. + * @return A map of loot keys and their weights. + */ + @NotNull Map getPossibleLootKeysWithWeight(Effect initialEffect, Condition condition); + + /** + * Get the next loot item based on fishing effect and condition. + * + * @param initialEffect The effect to apply weight modifiers. + * @param condition The condition to determine possible loot. + * @return The next loot item, or null if it doesn't exist. + */ + @Nullable Loot getNextLoot(Effect initialEffect, Condition condition); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/MarketManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/MarketManager.java index 57ab8045..942febd4 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/MarketManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/MarketManager.java @@ -21,15 +21,85 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; public interface MarketManager { + + /** + * Open the market GUI for a player + * + * @param player player + */ void openMarketGUI(Player player); + /** + * Retrieves the current date as an integer in the format MMDD (e.g., September 21 as 0921). + * + * @return An integer representing the current date. + */ int getDate(); + /** + * Calculates the price of an ItemStack based on custom data or a predefined price map. + * + * @param itemStack The ItemStack for which the price is calculated. + * @return The calculated price of the ItemStack. + */ double getItemPrice(ItemStack itemStack); + /** + * Retrieves the formula used for calculating prices. + * + * @return The pricing formula as a string. + */ String getFormula(); - double getPrice(float base, float bonus, float size); + /** + * Calculates the price based on a formula with provided variables. + * + * @param base The base value for the formula. + * @param bonus The bonus value for the formula. + * @param size The size value for the formula. + * @return The calculated price based on the formula and provided variables. + */ + double getFishPrice(float base, float bonus, float size); + /** + * Gets the character representing the item slot in the MarketGUI. + * + * @return The item slot character. + */ + char getItemSlot(); + + /** + * Gets the character representing the function slot in the MarketGUI. + * + * @return The function slot character. + */ + char getFunctionSlot(); + + /** + * Gets the layout of the MarketGUI as an array of strings. + * + * @return The layout of the MarketGUI. + */ + String[] getLayout(); + + /** + * Gets the title of the MarketGUI. + * + * @return The title of the MarketGUI. + */ + String getTitle(); + + /** + * Gets the earning limit + * + * @return The earning limit + */ + double getEarningLimit(); + + /** + * Is market enabled + * + * @return enable or not + */ boolean isEnable(); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/PlaceholderManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/PlaceholderManager.java index c568e28a..30ba554f 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/PlaceholderManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/PlaceholderManager.java @@ -26,17 +26,68 @@ import java.util.Map; public interface PlaceholderManager { + /** + * Set placeholders in a text string for a player. + * + * @param player The player for whom the placeholders should be set. + * @param text The text string containing placeholders. + * @return The text string with placeholders replaced if PlaceholderAPI is available; otherwise, the original text. + */ String setPlaceholders(Player player, String text); + /** + * Set placeholders in a text string for an offline player. + * + * @param player The offline player for whom the placeholders should be set. + * @param text The text string containing placeholders. + * @return The text string with placeholders replaced if PlaceholderAPI is available; otherwise, the original text. + */ String setPlaceholders(OfflinePlayer player, String text); + /** + * Detect and extract placeholders from a text string. + * + * @param text The text string to search for placeholders. + * @return A list of detected placeholders in the text. + */ List detectPlaceholders(String text); + /** + * Get the value associated with a single placeholder. + * + * @param player The player for whom the placeholders are being resolved (nullable). + * @param placeholder The placeholder to look up. + * @param placeholders A map of placeholders to their corresponding values. + * @return The value associated with the placeholder, or the original placeholder if not found. + */ String getSingleValue(@Nullable Player player, String placeholder, Map placeholders); + /** + * Parse a text string by replacing placeholders with their corresponding values. + * + * @param player The offline player for whom the placeholders are being resolved (nullable). + * @param text The text string containing placeholders. + * @param placeholders A map of placeholders to their corresponding values. + * @return The text string with placeholders replaced by their values. + */ String parse(@Nullable OfflinePlayer player, String text, Map placeholders); - String parseCacheable(Player player, String text); - + /** + * Parse a list of text strings by replacing placeholders with their corresponding values. + * + * @param player The player for whom the placeholders are being resolved (can be null for offline players). + * @param list The list of text strings containing placeholders. + * @param replacements A map of custom replacements for placeholders. + * @return The list of text strings with placeholders replaced by their values. + */ List parse(@Nullable OfflinePlayer player, List list, Map replacements); + + /** + * Parse a text string by replacing placeholders with their corresponding values, using caching for the player's placeholders. + * + * @param player The player for whom the placeholders are being resolved. + * @param text The text string containing placeholders. + * @return The text string with placeholders replaced by their values. + */ + String parseCacheablePlaceholders(Player player, String text);; } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/RequirementManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/RequirementManager.java index 55c93c79..6d61fe07 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/RequirementManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/RequirementManager.java @@ -21,29 +21,80 @@ import net.momirealms.customfishing.api.mechanic.condition.Condition; import net.momirealms.customfishing.api.mechanic.requirement.Requirement; import net.momirealms.customfishing.api.mechanic.requirement.RequirementFactory; import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; - public interface RequirementManager { + /** + * Registers a custom requirement type with its corresponding factory. + * + * @param type The type identifier of the requirement. + * @param requirementFactory The factory responsible for creating instances of the requirement. + * @return True if registration was successful, false if the type is already registered. + */ boolean registerRequirement(String type, RequirementFactory requirementFactory); + /** + * Unregisters a custom requirement type. + * + * @param type The type identifier of the requirement to unregister. + * @return True if unregistration was successful, false if the type is not registered. + */ boolean unregisterRequirement(String type); - HashMap getLootWithWeight(Condition condition); - - HashMap getGameWithWeight(Condition condition); - + /** + * Retrieves an array of requirements based on a configuration section. + * + * @param section The configuration section containing requirement definitions. + * @param advanced A flag indicating whether to use advanced requirements. + * @return An array of Requirement objects based on the configuration section + */ @Nullable Requirement[] getRequirements(ConfigurationSection section, boolean advanced); - Requirement getRequirement(ConfigurationSection section, boolean checkAction); + /** + * Retrieves a Requirement object based on a configuration section and advanced flag. + *

+ * requirement_1: <- section + * type: xxx + * value: xxx + * + * @param section The configuration section containing requirement definitions. + * @param advanced A flag indicating whether to use advanced requirements. + * @return A Requirement object based on the configuration section, or an EmptyRequirement if the section is null or invalid. + */ + @NotNull Requirement getRequirement(ConfigurationSection section, boolean advanced); - Requirement getRequirement(String key, Object value); + /** + * Gets a requirement based on the provided type and value. + * If a valid RequirementFactory is found for the type, it is used to create the requirement. + * If no factory is found, a warning is logged, and an empty requirement instance is returned. + *

+ * world: <- type + * - world <- value + * + * @param type The type representing the requirement type. + * @param value The value associated with the requirement. + * @return A Requirement instance based on the type and value, or an EmptyRequirement if the type is invalid. + */ + @NotNull Requirement getRequirement(String type, Object value); - RequirementFactory getRequirementBuilder(String type); + /** + * Retrieves a RequirementFactory based on the specified requirement type. + * + * @param type The requirement type for which to retrieve a factory. + * @return A RequirementFactory for the specified type, or null if no factory is found. + */ + @Nullable RequirementFactory getRequirementFactory(String type); - static boolean isRequirementsMet(Requirement[] requirements, Condition condition) { + /** + * Checks if an array of requirements is met for a given condition. + * + * @param condition The Condition object to check against the requirements. + * @param requirements An array of Requirement instances to be evaluated. + * @return True if all requirements are met, false otherwise. Returns true if the requirements array is null. + */ + static boolean isRequirementMet(Condition condition, Requirement... requirements) { if (requirements == null) return true; for (Requirement requirement : requirements) { if (!requirement.isConditionMet(condition)) { @@ -52,9 +103,4 @@ public interface RequirementManager { } return true; } - - static boolean isRequirementMet(Requirement requirement, Condition condition) { - if (requirement == null) return true; - return requirement.isConditionMet(condition); - } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/StatisticsManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/StatisticsManager.java index 7ae25b0a..64ad6544 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/StatisticsManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/StatisticsManager.java @@ -24,7 +24,20 @@ import java.util.List; import java.util.UUID; public interface StatisticsManager { - Statistics getStatistics(UUID uuid); + /** + * Get the statistics for a player with the given UUID. + * + * @param uuid The UUID of the player for whom statistics are retrieved. + * @return The player's statistics or null if the player is not found. + */ + @Nullable Statistics getStatistics(UUID uuid); + + /** + * Get a list of strings associated with a specific key in a category map. + * + * @param key The key to look up in the category map. + * @return A list of strings associated with the key or null if the key is not found. + */ @Nullable List getCategory(String key); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/StorageManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/StorageManager.java index 3d45e5a7..9d22369d 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/StorageManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/StorageManager.java @@ -22,6 +22,7 @@ import net.momirealms.customfishing.api.data.PlayerData; import net.momirealms.customfishing.api.data.user.OfflineUser; import net.momirealms.customfishing.api.data.user.OnlineUser; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Optional; import java.util.UUID; @@ -30,49 +31,93 @@ import java.util.concurrent.CompletableFuture; public interface StorageManager { /** - * Get server unique id - * @return id + * Gets the unique server identifier. + * + * @return The unique server identifier. */ - String getUniqueID(); + @NotNull String getUniqueID(); /** - * Get online user's data - * @param uuid uuid - * @return online user data + * Gets an OnlineUser instance for the specified UUID. + * + * @param uuid The UUID of the player. + * @return An OnlineUser instance if the player is online, or null if not. */ - OnlineUser getOnlineUser(UUID uuid); + @Nullable OnlineUser getOnlineUser(UUID uuid); /** - * Get an offline user's data - * Otherwise it would return Optional.empty() if data is locked - * It an offline user never played the server, its name would equal "" (empty string) - * @param uuid uuid - * @param lock lock - * @return offline user data + * Asynchronously retrieves an OfflineUser instance for the specified UUID. + * The offline user might be a locked one with no data, use isLockedData() method + * to check if it's an empty locked data + * + * @param uuid The UUID of the player. + * @param lock Whether to lock the data during retrieval. + * @return A CompletableFuture that resolves to an Optional containing the OfflineUser instance if found, or empty if not found or locked. */ CompletableFuture> getOfflineUser(UUID uuid, boolean lock); + /** + * If the offlineUser is locked with no data in it + * An user's data would be locked if he is playing on another server that connected + * to database. Modifying this data would actually do nothing. + * + * @param offlineUser offlineUser + * @return is locked or not + */ + boolean isLockedData(OfflineUser offlineUser); + + /** + * Asynchronously saves user data for an OfflineUser. + * + * @param offlineUser The OfflineUser whose data needs to be saved. + * @param unlock Whether to unlock the data after saving. + * @return A CompletableFuture that resolves to a boolean indicating the success of the data saving operation. + */ CompletableFuture saveUserData(OfflineUser offlineUser, boolean unlock); /** - * Get all the players in servers that connected to the same redis server - * @return amount - */ - CompletableFuture getRedisPlayerCount(); - - /** - * Get plugin data source - * @return data source + * Gets the data source used for data storage. + * + * @return The data source. */ DataStorageInterface getDataSource(); + /** + * Checks if Redis is enabled. + * + * @return True if Redis is enabled; otherwise, false. + */ boolean isRedisEnabled(); - byte[] toBytes(@NotNull PlayerData data); + /** + * Converts PlayerData to bytes. + * + * @param data The PlayerData to be converted. + * @return The byte array representation of PlayerData. + */ + byte @NotNull [] toBytes(@NotNull PlayerData data); + /** + * Converts PlayerData to JSON format. + * + * @param data The PlayerData to be converted. + * @return The JSON string representation of PlayerData. + */ @NotNull String toJson(@NotNull PlayerData data); - PlayerData fromJson(String json); + /** + * Converts JSON string to PlayerData. + * + * @param json The JSON string to be converted. + * @return The PlayerData object. + */ + @NotNull PlayerData fromJson(String json); + /** + * Converts bytes to PlayerData. + * + * @param data The byte array to be converted. + * @return The PlayerData object. + */ @NotNull PlayerData fromBytes(byte[] data); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/TotemManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/TotemManager.java index 6aa4031c..02c3dd9b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/TotemManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/TotemManager.java @@ -21,5 +21,12 @@ import net.momirealms.customfishing.api.mechanic.effect.EffectCarrier; import org.bukkit.Location; public interface TotemManager { + + /** + * Get the EffectCarrier associated with an activated totem located near the specified location. + * + * @param location The location to search for activated totems. + * @return The EffectCarrier associated with the nearest activated totem or null if none are found. + */ EffectCarrier getTotemEffect(Location location); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/AbstractCompetitionInfo.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/AbstractCompetitionInfo.java index 17945169..434395d7 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/AbstractCompetitionInfo.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/AbstractCompetitionInfo.java @@ -17,25 +17,49 @@ package net.momirealms.customfishing.api.mechanic.competition; +/** + * Abstract base class for competition information. + * Contains common properties and methods for competition info. + */ public abstract class AbstractCompetitionInfo { - + protected int refreshRate; protected int switchInterval; protected boolean showToAll; protected String[] texts; + /** + * Get the refresh rate for updating competition information. + * + * @return The refresh rate in ticks. + */ public int getRefreshRate() { return refreshRate; } + /** + * Get the switch interval for displaying different competition texts. + * + * @return The switch interval in ticks. + */ public int getSwitchInterval() { return switchInterval; } + /** + * Check if competition information should be shown to all players. + * + * @return True if information is shown to all players, otherwise only to participants. + */ public boolean isShowToAll() { return showToAll; } + /** + * Get an array of competition information texts. + * + * @return An array of competition information texts. + */ public String[] getTexts() { return texts; } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionConfig.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionConfig.java index a38d6e66..9836311b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionConfig.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionConfig.java @@ -18,6 +18,7 @@ package net.momirealms.customfishing.api.mechanic.competition; import net.momirealms.customfishing.api.mechanic.action.Action; +import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -43,11 +44,11 @@ public class CompetitionConfig { return key; } - public int getDuration() { + public int getDurationInSeconds() { return duration; } - public int getMinPlayers() { + public int getMinPlayersToStart() { return minPlayers; } @@ -59,10 +60,20 @@ public class CompetitionConfig { return endActions; } + /** + * Get the actions to perform if player joined the competition + * + * @return actions + */ public Action[] getJoinActions() { return joinActions; } + /** + * Get the actions to perform if the amount of players doesn't meet the requirement + * + * @return actions + */ public Action[] getSkipActions() { return skipActions; } @@ -71,18 +82,29 @@ public class CompetitionConfig { return goal; } + /** + * Get the reward map + * + * @return reward map + */ public HashMap getRewards() { return rewards; } + @Nullable public BossBarConfig getBossBarConfig() { return bossBarConfig; } + @Nullable public ActionBarConfig getActionBarConfig() { return actionBarConfig; } + public static Builder builder(String key) { + return new Builder(key); + } + public static class Builder { private final CompetitionConfig config; @@ -121,11 +143,13 @@ public class CompetitionConfig { return this; } + @SuppressWarnings("UnusedReturnValue") public Builder actionbar(ActionBarConfig actionBarConfig) { config.actionBarConfig = actionBarConfig; return this; } + @SuppressWarnings("UnusedReturnValue") public Builder bossbar(BossBarConfig bossBarConfig) { config.bossBarConfig = bossBarConfig; return this; diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/FishingCompetition.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/FishingCompetition.java index 72d95ec0..f9685e19 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/FishingCompetition.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/FishingCompetition.java @@ -19,35 +19,106 @@ package net.momirealms.customfishing.api.mechanic.competition; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Map; public interface FishingCompetition { + + /** + * Start the fishing competition + */ void start(); + /** + * Stop the fishing competition + */ void stop(); + /** + * End the fishing competition + */ void end(); + /** + * Check if the fishing competition is ongoing. + * + * @return {@code true} if the competition is still ongoing, {@code false} if it has ended. + */ boolean isOnGoing(); + /** + * Refreshes the data for a player in the fishing competition, including updating their score and triggering + * actions if it's their first time joining the competition. + * + * @param player The player whose data needs to be refreshed. + * @param score The player's current score in the competition. + */ void refreshData(Player player, double score); + /** + * Checks if a player has joined the fishing competition based on their name. + * + * @param player The player to check for participation. + * @return {@code true} if the player has joined the competition; {@code false} otherwise. + */ boolean hasPlayerJoined(OfflinePlayer player); + /** + * Gets the progress of the fishing competition as a float value (0~1). + * + * @return The progress of the fishing competition as a float. + */ float getProgress(); + /** + * Gets the remaining time in seconds for the fishing competition. + * + * @return The remaining time in seconds. + */ long getRemainingTime(); + /** + * Gets the start time of the fishing competition. + * + * @return The start time of the fishing competition. + */ long getStartTime(); - CompetitionConfig getConfig(); + /** + * Gets the configuration of the fishing competition. + * + * @return The configuration of the fishing competition. + */ + @NotNull CompetitionConfig getConfig(); - CompetitionGoal getGoal(); + /** + * Gets the goal of the fishing competition. + * + * @return The goal of the fishing competition. + */ + @NotNull CompetitionGoal getGoal(); - Ranking getRanking(); + /** + * Gets the ranking data for the fishing competition. + * + * @return The ranking data for the fishing competition. + */ + @NotNull Ranking getRanking(); - ConcurrentHashMap getCachedPlaceholders(); + /** + * Gets the cached placeholders for the fishing competition. + * + * @return A ConcurrentHashMap containing cached placeholders. + */ + @NotNull Map getCachedPlaceholders(); - String getCachedPlaceholder(String papi); + /** + * Gets a specific cached placeholder value by its key. + * + * @param papi The key of the cached placeholder. + * @return The cached placeholder value as a string, or null if not found. + */ + @Nullable String getCachedPlaceholder(String papi); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/condition/FishingPreparation.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/condition/FishingPreparation.java index 545cd18d..0c23dd5f 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/condition/FishingPreparation.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/condition/FishingPreparation.java @@ -56,8 +56,8 @@ public class FishingPreparation extends Condition { this.effects = new ArrayList<>(); boolean rodOnMainHand = mainHandItem.getType() == Material.FISHING_ROD; this.rodItemStack = rodOnMainHand ? mainHandItem : offHandItem; - String rodItemID = plugin.getItemManager().getAnyItemID(this.rodItemStack); - EffectCarrier rodEffect = plugin.getEffectManager().getEffect("rod", rodItemID); + String rodItemID = plugin.getItemManager().getAnyPluginItemID(this.rodItemStack); + EffectCarrier rodEffect = plugin.getEffectManager().getEffectCarrier("rod", rodItemID); if (rodEffect != null) effects.add(rodEffect); super.insertArg("{rod}", rodItemID); @@ -66,14 +66,14 @@ public class FishingPreparation extends Condition { if (cfCompound != null && cfCompound.hasTag("hook_id")) { String hookID = cfCompound.getString("hook_id"); super.insertArg("{hook}", rodItemID); - EffectCarrier carrier = plugin.getEffectManager().getEffect("hook", hookID); + EffectCarrier carrier = plugin.getEffectManager().getEffectCarrier("hook", hookID); if (carrier != null) { this.effects.add(carrier); } } - String baitItemID = plugin.getItemManager().getAnyItemID(rodOnMainHand ? offHandItem : mainHandItem); - EffectCarrier baitEffect = plugin.getEffectManager().getEffect("bait", baitItemID); + String baitItemID = plugin.getItemManager().getAnyPluginItemID(rodOnMainHand ? offHandItem : mainHandItem); + EffectCarrier baitEffect = plugin.getEffectManager().getEffectCarrier("bait", baitItemID); if (baitEffect != null) { this.baitItemStack = rodOnMainHand ? offHandItem : mainHandItem; @@ -89,10 +89,10 @@ public class FishingPreparation extends Condition { this.insertArg("{in-bag}", "true"); for (int i = 0; i < fishingBag.getSize(); i++) { ItemStack itemInBag = fishingBag.getItem(i); - String bagItemID = plugin.getItemManager().getItemID(itemInBag); + String bagItemID = plugin.getItemManager().getCustomFishingItemID(itemInBag); if (bagItemID == null) continue; if (!hasBait) { - EffectCarrier effect = plugin.getEffectManager().getEffect("bait", bagItemID); + EffectCarrier effect = plugin.getEffectManager().getEffectCarrier("bait", bagItemID); if (effect != null) { this.baitItemStack = itemInBag; this.effects.add(effect); @@ -100,7 +100,7 @@ public class FishingPreparation extends Condition { continue; } } - EffectCarrier utilEffect = plugin.getEffectManager().getEffect("util", bagItemID); + EffectCarrier utilEffect = plugin.getEffectManager().getEffectCarrier("util", bagItemID); if (utilEffect != null && !uniqueUtils.contains(bagItemID)) { effects.add(utilEffect); uniqueUtils.add(bagItemID); @@ -111,7 +111,7 @@ public class FishingPreparation extends Condition { } for (String enchant : plugin.getIntegrationManager().getEnchantments(rodItemStack)) { - EffectCarrier enchantEffect = plugin.getEffectManager().getEffect("enchant", enchant); + EffectCarrier enchantEffect = plugin.getEffectManager().getEffectCarrier("enchant", enchant); if (enchantEffect != null) { this.effects.add(enchantEffect); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/BasicGameConfig.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/BasicGameConfig.java index 01a4df5d..acd1233c 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/BasicGameConfig.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/BasicGameConfig.java @@ -29,6 +29,10 @@ public class BasicGameConfig { private int minDifficulty; private int maxDifficulty; + public static Builder builder() { + return new Builder(); + } + public static class Builder { private final BasicGameConfig basicGameConfig; @@ -37,11 +41,13 @@ public class BasicGameConfig { basicGameConfig = new BasicGameConfig(); } + @SuppressWarnings("UnusedReturnValue") public Builder difficulty(int value) { basicGameConfig.minDifficulty = (basicGameConfig.maxDifficulty = value); return this; } + @SuppressWarnings("UnusedReturnValue") public Builder difficulty(int min, int max) { basicGameConfig.minDifficulty = min; basicGameConfig.maxDifficulty = max; @@ -64,6 +70,12 @@ public class BasicGameConfig { } } + /** + * Generates random game settings based on specified time and difficulty ranges, adjusted by an effect's difficulty modifier. + * + * @param effect The effect to adjust the difficulty. + * @return A {@link GameSettings} object representing the generated game settings. + */ @Nullable public GameSettings getGameSetting(Effect effect) { return new GameSettings( diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GamingPlayer.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GamingPlayer.java index 90b033ca..4c1cd9f0 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GamingPlayer.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GamingPlayer.java @@ -19,24 +19,46 @@ package net.momirealms.customfishing.api.mechanic.game; import net.momirealms.customfishing.api.mechanic.effect.Effect; import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; public interface GamingPlayer { + /** + * Cancel the game + */ void cancel(); boolean isSuccessful(); + /** + * @return whether to cancel the event + */ boolean onRightClick(); + /** + * @return whether to cancel the event + */ boolean onLeftClick(); + /** + * @return whether to cancel the event + */ boolean onSwapHand(); + /** + * @return whether to cancel the event + */ + boolean onChat(String message); + + /** + * @return whether to cancel the event + */ boolean onJump(); Player getPlayer(); - Effect getEffectReward(); - - boolean onChat(String message); + /** + * @return effect reward based on game results + */ + @Nullable Effect getEffectReward(); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java b/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java index 2957a191..ed40ea8d 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java @@ -137,6 +137,9 @@ public class CustomFishingPluginImpl extends CustomFishingPlugin { HandlerList.unregisterAll(this); } + /** + * Reload the plugin + */ @Override public void reload() { CFConfig.load(); @@ -181,6 +184,9 @@ public class CustomFishingPluginImpl extends CustomFishingPlugin { this.coolDownManager.load(); } + /** + * Load plugin dependencies + */ private void loadDependencies() { String mavenRepo = TimeZone.getDefault().getID().startsWith("Asia") ? "https://maven.aliyun.com/repository/public/" : "https://repo.maven.apache.org/maven2/"; @@ -203,6 +209,9 @@ public class CustomFishingPluginImpl extends CustomFishingPlugin { ); } + /** + * Disable NBT API logs + */ private void disableNBTAPILogs() { MinecraftVersion.disableBStats(); MinecraftVersion.disableUpdateCheck(); @@ -236,6 +245,12 @@ public class CustomFishingPluginImpl extends CustomFishingPlugin { } } + /** + * Retrieves a YAML configuration from a file within the plugin's data folder. + * + * @param file The name of the configuration file. + * @return A YamlConfiguration object representing the configuration. + */ @Override public YamlConfiguration getConfig(String file) { File config = new File(this.getDataFolder(), file); @@ -243,23 +258,44 @@ public class CustomFishingPluginImpl extends CustomFishingPlugin { return YamlConfiguration.loadConfiguration(config); } + /** + * Checks if a specified plugin is enabled on the Bukkit server. + * + * @param plugin The name of the plugin to check. + * @return True if the plugin is enabled, false otherwise. + */ @Override public boolean isHookedPluginEnabled(String plugin) { return Bukkit.getPluginManager().isPluginEnabled(plugin); } - @NotNull - public static ProtocolManager getProtocolManager() { - return protocolManager; - } - + /** + * Outputs a debugging message if the debug mode is enabled. + * + * @param message The debugging message to be logged. + */ @Override public void debug(String message) { if (!CFConfig.debug) return; LogUtils.info(message); } + /** + * Gets the CoolDownManager instance associated with the plugin. + * + * @return The CoolDownManager instance. + */ public CoolDownManager getCoolDownManager() { return coolDownManager; } + + /** + * Retrieves the ProtocolManager instance used for managing packets. + * + * @return The ProtocolManager instance. + */ + @NotNull + public static ProtocolManager getProtocolManager() { + return protocolManager; + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/command/sub/CompetitionCommand.java b/plugin/src/main/java/net/momirealms/customfishing/command/sub/CompetitionCommand.java index ec0a6b50..9f8afc1a 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/command/sub/CompetitionCommand.java +++ b/plugin/src/main/java/net/momirealms/customfishing/command/sub/CompetitionCommand.java @@ -47,7 +47,7 @@ public class CompetitionCommand { } private CommandAPICommand getCompetitionStartCommand() { - Set allCompetitions = CustomFishingPlugin.get().getCompetitionManager().getAllCompetitions(); + Set allCompetitions = CustomFishingPlugin.get().getCompetitionManager().getAllCompetitionKeys(); var command = new CommandAPICommand("start") .withArguments( new StringArgument("id") diff --git a/plugin/src/main/java/net/momirealms/customfishing/command/sub/DebugCommand.java b/plugin/src/main/java/net/momirealms/customfishing/command/sub/DebugCommand.java index b4d049a2..57991597 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/command/sub/DebugCommand.java +++ b/plugin/src/main/java/net/momirealms/customfishing/command/sub/DebugCommand.java @@ -120,7 +120,7 @@ public class DebugCommand { for (EffectModifier modifier : totemEffect.getEffectModifiers()) { modifier.modify(initialEffect, fishingPreparation); } - var map = CustomFishingPlugin.get().getFishingManager().getPossibleLootKeysWithWeight(initialEffect, fishingPreparation); + var map = CustomFishingPlugin.get().getLootManager().getPossibleLootKeysWithWeight(initialEffect, fishingPreparation); List loots = new ArrayList<>(); double sum = 0; for (Map.Entry entry : map.entrySet()) { diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/IntegrationManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/compatibility/IntegrationManagerImpl.java index 4f4e3cfd..18b8e6bb 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/IntegrationManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/compatibility/IntegrationManagerImpl.java @@ -48,18 +48,18 @@ public class IntegrationManagerImpl implements IntegrationManager { private final CustomFishingPlugin plugin; private final HashMap levelPluginMap; - private final HashMap enchantments; + private final HashMap enchantmentPluginMap; private SeasonInterface seasonInterface; public IntegrationManagerImpl(CustomFishingPlugin plugin) { this.plugin = plugin; this.levelPluginMap = new HashMap<>(); - this.enchantments = new HashMap<>(); + this.enchantmentPluginMap = new HashMap<>(); this.load(); } public void disable() { - this.enchantments.clear(); + this.enchantmentPluginMap.clear(); this.levelPluginMap.clear(); } @@ -121,13 +121,13 @@ public class IntegrationManagerImpl implements IntegrationManager { hookMessage("AureliumSkills"); } if (plugin.isHookedPluginEnabled("EcoEnchants")) { - this.enchantments.put("EcoEnchants", new VanillaEnchantmentsImpl()); + this.enchantmentPluginMap.put("EcoEnchants", new VanillaEnchantmentsImpl()); hookMessage("EcoEnchants"); } else { - this.enchantments.put("vanilla", new VanillaEnchantmentsImpl()); + this.enchantmentPluginMap.put("vanilla", new VanillaEnchantmentsImpl()); } if (plugin.isHookedPluginEnabled("AdvancedEnchantments")) { - this.enchantments.put("AdvancedEnchantments", new AdvancedEnchantmentsImpl()); + this.enchantmentPluginMap.put("AdvancedEnchantments", new AdvancedEnchantmentsImpl()); hookMessage("AdvancedEnchantments"); } if (plugin.isHookedPluginEnabled("RealisticSeasons")) { @@ -161,6 +161,13 @@ public class IntegrationManagerImpl implements IntegrationManager { // } } + /** + * Registers a level plugin with the specified name. + * + * @param plugin The name of the level plugin. + * @param level The implementation of the LevelInterface. + * @return true if the registration was successful, false if the plugin name is already registered. + */ @Override public boolean registerLevelPlugin(String plugin, LevelInterface level) { if (levelPluginMap.containsKey(plugin)) return false; @@ -168,46 +175,100 @@ public class IntegrationManagerImpl implements IntegrationManager { return true; } + /** + * Unregisters a level plugin with the specified name. + * + * @param plugin The name of the level plugin to unregister. + * @return true if the unregistration was successful, false if the plugin name is not found. + */ @Override public boolean unregisterLevelPlugin(String plugin) { return levelPluginMap.remove(plugin) != null; } + /** + * Registers an enchantment provided by a plugin. + * + * @param plugin The name of the plugin providing the enchantment. + * @param enchantment The enchantment to register. + * @return true if the registration was successful, false if the enchantment name is already in use. + */ @Override public boolean registerEnchantment(String plugin, EnchantmentInterface enchantment) { - if (enchantments.containsKey(plugin)) return false; - enchantments.put(plugin, enchantment); + if (enchantmentPluginMap.containsKey(plugin)) return false; + enchantmentPluginMap.put(plugin, enchantment); return true; } + /** + * Unregisters an enchantment provided by a plugin. + * + * @param plugin The name of the plugin providing the enchantment. + * @return true if the enchantment was successfully unregistered, false if the enchantment was not found. + */ @Override public boolean unregisterEnchantment(String plugin) { - return enchantments.remove(plugin) != null; + return enchantmentPluginMap.remove(plugin) != null; } private void hookMessage(String plugin) { LogUtils.info( plugin + " hooked!"); } + /** + * Get the LevelInterface provided by a plugin. + * + * @param plugin The name of the plugin providing the LevelInterface. + * @return The LevelInterface provided by the specified plugin, or null if the plugin is not registered. + */ @Override - public LevelInterface getLevelHook(String plugin) { + @Nullable + public LevelInterface getLevelPlugin(String plugin) { return levelPluginMap.get(plugin); } + /** + * Get an enchantment plugin by its plugin name. + * + * @param plugin The name of the enchantment plugin. + * @return The enchantment plugin interface, or null if not found. + */ + @Override + @Nullable + public EnchantmentInterface getEnchantmentPlugin(String plugin) { + return enchantmentPluginMap.get(plugin); + } + + /** + * Get a list of enchantment keys with level applied to the given ItemStack. + * + * @param itemStack The ItemStack to check for enchantments. + * @return A list of enchantment names applied to the ItemStack. + */ @Override public List getEnchantments(ItemStack itemStack) { ArrayList list = new ArrayList<>(); - for (EnchantmentInterface enchantmentInterface : enchantments.values()) { + for (EnchantmentInterface enchantmentInterface : enchantmentPluginMap.values()) { list.addAll(enchantmentInterface.getEnchants(itemStack)); } return list; } + /** + * Get the current season interface, if available. + * + * @return The current season interface, or null if not available. + */ @Nullable public SeasonInterface getSeasonInterface() { return seasonInterface; } + /** + * Set the current season interface. + * + * @param season The season interface to set. + */ @Override public void setSeasonInterface(SeasonInterface season) { this.seasonInterface = season; diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/CustomFishingItemImpl.java b/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/CustomFishingItemImpl.java index fdfb2ea3..a754e006 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/CustomFishingItemImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/CustomFishingItemImpl.java @@ -37,6 +37,6 @@ public class CustomFishingItemImpl implements ItemLibrary { @Override public String getItemID(ItemStack itemStack) { - return CustomFishingPlugin.get().getItemManager().getItemID(itemStack); + return CustomFishingPlugin.get().getItemManager().getCustomFishingItemID(itemStack); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/PlaceholderManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/PlaceholderManagerImpl.java index e54cf7ac..b05401a9 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/PlaceholderManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/PlaceholderManagerImpl.java @@ -92,16 +92,36 @@ public class PlaceholderManagerImpl implements PlaceholderManager, Listener { } } + /** + * Set placeholders in a text string for a player. + * + * @param player The player for whom the placeholders should be set. + * @param text The text string containing placeholders. + * @return The text string with placeholders replaced if PlaceholderAPI is available; otherwise, the original text. + */ @Override public String setPlaceholders(Player player, String text) { return hasPapi ? ParseUtils.setPlaceholders(player, text) : text; } + /** + * Set placeholders in a text string for an offline player. + * + * @param player The offline player for whom the placeholders should be set. + * @param text The text string containing placeholders. + * @return The text string with placeholders replaced if PlaceholderAPI is available; otherwise, the original text. + */ @Override public String setPlaceholders(OfflinePlayer player, String text) { return hasPapi ? ParseUtils.setPlaceholders(player, text) : text; } + /** + * Detect and extract placeholders from a text string. + * + * @param text The text string to search for placeholders. + * @return A list of detected placeholders in the text. + */ @Override public List detectPlaceholders(String text) { List placeholders = new ArrayList<>(); @@ -110,6 +130,14 @@ public class PlaceholderManagerImpl implements PlaceholderManager, Listener { return placeholders; } + /** + * Get the value associated with a single placeholder. + * + * @param player The player for whom the placeholders are being resolved (nullable). + * @param placeholder The placeholder to look up. + * @param placeholders A map of placeholders to their corresponding values. + * @return The value associated with the placeholder, or the original placeholder if not found. + */ @Override public String getSingleValue(@Nullable Player player, String placeholder, Map placeholders) { String result = null; @@ -123,6 +151,14 @@ public class PlaceholderManagerImpl implements PlaceholderManager, Listener { return setPlaceholders(player, custom); } + /** + * Parse a text string by replacing placeholders with their corresponding values. + * + * @param player The offline player for whom the placeholders are being resolved (nullable). + * @param text The text string containing placeholders. + * @param placeholders A map of placeholders to their corresponding values. + * @return The text string with placeholders replaced by their values. + */ @Override public String parse(@Nullable OfflinePlayer player, String text, Map placeholders) { var list = detectPlaceholders(text); @@ -144,8 +180,30 @@ public class PlaceholderManagerImpl implements PlaceholderManager, Listener { return text; } + /** + * Parse a list of text strings by replacing placeholders with their corresponding values. + * + * @param player The player for whom the placeholders are being resolved (can be null for offline players). + * @param list The list of text strings containing placeholders. + * @param replacements A map of custom replacements for placeholders. + * @return The list of text strings with placeholders replaced by their values. + */ @Override - public String parseCacheable(Player player, String text) { + public List parse(@Nullable OfflinePlayer player, List list, Map replacements) { + return list.stream() + .map(s -> parse(player, s, replacements)) + .collect(Collectors.toList()); + } + + /** + * Parse a text string by replacing placeholders with their corresponding values, using caching for the player's placeholders. + * + * @param player The player for whom the placeholders are being resolved. + * @param text The text string containing placeholders. + * @return The text string with placeholders replaced by their values. + */ + @Override + public String parseCacheablePlaceholders(Player player, String text) { var list = detectPlaceholders(text); CachedPlaceholder cachedPlaceholder = cachedPlaceholders.computeIfAbsent(player.getUniqueId(), k -> new CachedPlaceholder()); for (String papi : list) { @@ -157,13 +215,6 @@ public class PlaceholderManagerImpl implements PlaceholderManager, Listener { return text; } - @Override - public List parse(@Nullable OfflinePlayer player, List list, Map replacements) { - return list.stream() - .map(s -> parse(player, s, replacements)) - .collect(Collectors.toList()); - } - public static PlaceholderManagerImpl getInstance() { return instance; } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java index 721ad13e..b6297520 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java @@ -29,6 +29,7 @@ import net.momirealms.customfishing.api.mechanic.action.Action; import net.momirealms.customfishing.api.mechanic.action.ActionExpansion; import net.momirealms.customfishing.api.mechanic.action.ActionFactory; import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; +import net.momirealms.customfishing.api.mechanic.condition.Condition; import net.momirealms.customfishing.api.mechanic.loot.Loot; import net.momirealms.customfishing.api.mechanic.requirement.Requirement; import net.momirealms.customfishing.api.util.LogUtils; @@ -47,6 +48,7 @@ import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; @@ -68,6 +70,7 @@ public class ActionManagerImpl implements ActionManager { this.registerInbuiltActions(); } + // Method to register various built-in actions during initialization. private void registerInbuiltActions() { this.registerMessageAction(); this.registerCommandAction(); @@ -94,6 +97,7 @@ public class ActionManagerImpl implements ActionManager { this.registerMoneyAction(); } + // Method to load expansions and global event actions. public void load() { this.loadExpansions(); this.loadGlobalEventActions(); @@ -108,11 +112,20 @@ public class ActionManagerImpl implements ActionManager { this.actionBuilderMap.clear(); } + // Method to load global event actions from the plugin's configuration file. private void loadGlobalEventActions() { YamlConfiguration config = plugin.getConfig("config.yml"); GlobalSettings.load(config.getConfigurationSection("mechanics.global-events")); } + /** + * Registers an ActionFactory for a specific action type. + * This method allows you to associate an ActionFactory with a custom action type. + * + * @param type The custom action type to register. + * @param actionFactory The ActionFactory responsible for creating actions of the specified type. + * @return True if the registration was successful (the action type was not already registered), false otherwise. + */ @Override public boolean registerAction(String type, ActionFactory actionFactory) { if (this.actionBuilderMap.containsKey(type)) return false; @@ -120,36 +133,92 @@ public class ActionManagerImpl implements ActionManager { return true; } + /** + * Unregisters an ActionFactory for a specific action type. + * This method allows you to remove the association between an action type and its ActionFactory. + * + * @param type The custom action type to unregister. + * @return True if the action type was successfully unregistered, false if it was not found. + */ @Override public boolean unregisterAction(String type) { return this.actionBuilderMap.remove(type) != null; } + /** + * Retrieves an Action object based on the configuration provided in a ConfigurationSection. + * This method reads the type of action from the section, obtains the corresponding ActionFactory, + * and builds an Action object using the specified values and chance. + * + * @param section The ConfigurationSection containing the action configuration. + * @return An Action object created based on the configuration, or an EmptyAction instance if the action type is invalid. + */ @Override + @NotNull public Action getAction(ConfigurationSection section) { - return getActionBuilder(section.getString("type")).build(section.get("value"), section.getDouble("chance", 1d)); + ActionFactory factory = getActionFactory(section.getString("type")); + if (factory == null) { + LogUtils.warn("Action type: " + section.getString("type") + " doesn't exist."); + // to prevent NPE + return EmptyAction.instance; + } + return factory.build( + section.get("value"), + section.getDouble("chance", 1d) + ); } + /** + * Retrieves a mapping of ActionTriggers to arrays of Actions from a ConfigurationSection. + * This method iterates through the provided ConfigurationSection to extract action triggers + * and their associated arrays of Actions. + * + * @param section The ConfigurationSection containing action mappings. + * @return A HashMap where keys are ActionTriggers and values are arrays of Action objects. + */ @Override + @NotNull public HashMap getActionMap(ConfigurationSection section) { + // Create an empty HashMap to store the action mappings HashMap actionMap = new HashMap<>(); + + // If the provided ConfigurationSection is null, return the empty actionMap if (section == null) return actionMap; + + // Iterate through all key-value pairs in the ConfigurationSection for (Map.Entry entry : section.getValues(false).entrySet()) { if (entry.getValue() instanceof ConfigurationSection innerSection) { - actionMap.put( - ActionTrigger.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)), - getActions(innerSection) - ); + // Convert the key to an ActionTrigger enum (assuming it's in uppercase English) + // and map it to an array of Actions obtained from the inner section + try { + actionMap.put( + ActionTrigger.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)), + getActions(innerSection) + ); + } catch (IllegalArgumentException e) { + LogUtils.warn("Event: " + entry.getKey() + " doesn't exist!"); + } } } return actionMap; } - @Nullable + /** + * Retrieves an array of Action objects from a ConfigurationSection. + * This method iterates through the provided ConfigurationSection to extract Action configurations + * and build an array of Action objects. + * + * @param section The ConfigurationSection containing action configurations. + * @return An array of Action objects created based on the configurations in the section. + */ + @NotNull @Override public Action[] getActions(ConfigurationSection section) { - if (section == null) return null; + // Create an ArrayList to store the Actions ArrayList actionList = new ArrayList<>(); + if (section == null) return actionList.toArray(new Action[0]); + + // Iterate through all key-value pairs in the ConfigurationSection for (Map.Entry entry : section.getValues(false).entrySet()) { if (entry.getValue() instanceof ConfigurationSection innerSection) { actionList.add(getAction(innerSection)); @@ -158,11 +227,86 @@ public class ActionManagerImpl implements ActionManager { return actionList.toArray(new Action[0]); } + /** + * Retrieves an ActionFactory associated with a specific action type. + * + * @param type The action type for which to retrieve the ActionFactory. + * @return The ActionFactory associated with the specified action type, or null if not found. + */ + @Nullable @Override - public ActionFactory getActionBuilder(String type) { + public ActionFactory getActionFactory(String type) { return actionBuilderMap.get(type); } + /** + * Loads custom ActionExpansions from JAR files located in the expansion directory. + * This method scans the expansion folder for JAR files, loads classes that extend ActionExpansion, + * and registers them with the appropriate action type and ActionFactory. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + private void loadExpansions() { + File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); + if (!expansionFolder.exists()) + expansionFolder.mkdirs(); + + List> classes = new ArrayList<>(); + File[] expansionJars = expansionFolder.listFiles(); + if (expansionJars == null) return; + for (File expansionJar : expansionJars) { + if (expansionJar.getName().endsWith(".jar")) { + try { + Class expansionClass = ClassUtils.findClass(expansionJar, ActionExpansion.class); + classes.add(expansionClass); + } catch (IOException | ClassNotFoundException e) { + LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); + } + } + } + try { + for (Class expansionClass : classes) { + ActionExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); + unregisterAction(expansion.getActionType()); + registerAction(expansion.getActionType(), expansion.getActionFactory()); + LogUtils.info("Loaded action expansion: " + expansion.getActionType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor() ); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + LogUtils.warn("Error occurred when creating expansion instance.", e); + } + } + + /** + * Retrieves a mapping of success times to corresponding arrays of actions from a ConfigurationSection. + * + * @param section The ConfigurationSection containing success times actions. + * @return A HashMap where success times associated with actions. + */ + @Override + public HashMap getTimesActionMap(ConfigurationSection section) { + HashMap actionMap = new HashMap<>(); + if (section == null) return actionMap; + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + actionMap.put(Integer.parseInt(entry.getKey()), plugin.getActionManager().getActions(innerSection)); + } + } + return actionMap; + } + + /** + * Triggers a list of actions with the given condition. + * If the list of actions is not null, each action in the list is triggered. + * + * @param actions The list of actions to trigger. + * @param condition The condition associated with the actions. + */ + @Override + public void triggerActions(List actions, Condition condition) { + if (actions != null) + for (Action action : actions) + action.trigger(condition); + } + private void registerMessageAction() { registerAction("message", (args, chance) -> { ArrayList msg = ConfigUtils.stringListArgs(args); @@ -462,9 +606,9 @@ public class ActionManagerImpl implements ActionManager { Player player = condition.getPlayer(); ItemStack itemStack = player.getInventory().getItem(slot); if (amount > 0) { - ItemUtils.addDurability(itemStack, amount, true); + ItemUtils.increaseDurability(itemStack, amount, true); } else { - ItemUtils.loseDurability(itemStack, -amount, true); + ItemUtils.decreaseDurability(itemStack, -amount, true); } }; } else { @@ -482,7 +626,7 @@ public class ActionManagerImpl implements ActionManager { return condition -> { if (Math.random() > chance) return; Player player = condition.getPlayer(); - ItemUtils.giveCertainAmountOfItem(player, CustomFishingPlugin.get().getItemManager().buildAnyItemByID(player, id), amount); + ItemUtils.giveCertainAmountOfItem(player, CustomFishingPlugin.get().getItemManager().buildAnyPluginItemByID(player, id), amount); }; } else { LogUtils.warn("Illegal value format found at action: give-item"); @@ -780,7 +924,7 @@ public class ActionManagerImpl implements ActionManager { String target = section.getString("target"); return condition -> { if (Math.random() > chance) return; - Optional.ofNullable(plugin.getIntegrationManager().getLevelHook(pluginName)).ifPresentOrElse(it -> { + Optional.ofNullable(plugin.getIntegrationManager().getLevelPlugin(pluginName)).ifPresentOrElse(it -> { it.addXp(condition.getPlayer(), target, exp); }, () -> LogUtils.warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation.")); }; @@ -796,7 +940,7 @@ public class ActionManagerImpl implements ActionManager { if (Math.random() > chance) return; condition.insertArg("{lava}", String.valueOf(arg)); LootManager lootManager = plugin.getLootManager(); - List loots = plugin.getFishingManager().getPossibleLootKeys(condition).stream().map(lootManager::getLoot).filter(Objects::nonNull).filter(Loot::showInFinder).map(Loot::getNick).toList(); + List loots = plugin.getLootManager().getPossibleLootKeys(condition).stream().map(lootManager::getLoot).filter(Objects::nonNull).filter(Loot::showInFinder).map(Loot::getNick).toList(); StringJoiner stringJoiner = new StringJoiner(CFLocale.MSG_Split_Char); for (String loot : loots) { stringJoiner.add(loot); @@ -806,35 +950,4 @@ public class ActionManagerImpl implements ActionManager { }; }); } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private void loadExpansions() { - File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); - if (!expansionFolder.exists()) - expansionFolder.mkdirs(); - - List> classes = new ArrayList<>(); - File[] expansionJars = expansionFolder.listFiles(); - if (expansionJars == null) return; - for (File expansionJar : expansionJars) { - if (expansionJar.getName().endsWith(".jar")) { - try { - Class expansionClass = ClassUtils.findClass(expansionJar, ActionExpansion.class); - classes.add(expansionClass); - } catch (IOException | ClassNotFoundException e) { - LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); - } - } - } - try { - for (Class expansionClass : classes) { - ActionExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); - unregisterAction(expansion.getActionType()); - registerAction(expansion.getActionType(), expansion.getActionFactory()); - LogUtils.info("Loaded action expansion: " + expansion.getActionType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor() ); - } - } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { - LogUtils.warn("Error occurred when creating expansion instance.", e); - } - } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/EmptyAction.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/EmptyAction.java new file mode 100644 index 00000000..27011f2b --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/EmptyAction.java @@ -0,0 +1,17 @@ +package net.momirealms.customfishing.mechanic.action; + +import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.condition.Condition; + +/** + * An implementation of the Action interface that represents an empty action with no behavior. + * This class serves as a default action to prevent NPE. + */ +public class EmptyAction implements Action { + + public static EmptyAction instance = new EmptyAction(); + + @Override + public void trigger(Condition condition) { + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java index 75948517..ae04d769 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java @@ -21,6 +21,7 @@ import net.momirealms.customfishing.CustomFishingPluginImpl; import net.momirealms.customfishing.api.CustomFishingPlugin; import net.momirealms.customfishing.api.data.user.OfflineUser; import net.momirealms.customfishing.api.manager.BagManager; +import net.momirealms.customfishing.api.manager.EffectManager; import net.momirealms.customfishing.api.mechanic.bag.FishingBagHolder; import net.momirealms.customfishing.setting.CFConfig; import org.bukkit.Bukkit; @@ -34,6 +35,7 @@ import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; import java.util.HashMap; import java.util.UUID; @@ -66,6 +68,13 @@ public class BagManagerImpl implements BagManager, Listener { plugin.getStorageManager().getDataSource().updateManyPlayersData(tempEditMap.values(), true); } + /** + * Retrieves the online bag inventory associated with a player's UUID. + * + * @param uuid The UUID of the player for whom the bag inventory is retrieved. + * @return The online bag inventory if the player is online, or null if not found. + */ + @Nullable @Override public Inventory getOnlineBagInventory(UUID uuid) { var onlinePlayer = plugin.getStorageManager().getOnlineUser(uuid); @@ -75,12 +84,23 @@ public class BagManagerImpl implements BagManager, Listener { return onlinePlayer.getHolder().getInventory(); } + /** + * Initiates the process of editing the bag inventory of an offline player by an admin. + * + * @param admin The admin player performing the edit. + * @param userData The OfflineUser data of the player whose bag is being edited. + */ @Override public void editOfflinePlayerBag(Player admin, OfflineUser userData) { this.tempEditMap.put(admin.getUniqueId(), userData); admin.openInventory(userData.getHolder().getInventory()); } + /** + * Handles the InventoryCloseEvent to save changes made to an offline player's bag inventory when it's closed. + * + * @param event The InventoryCloseEvent triggered when the inventory is closed. + */ @EventHandler public void onInvClose(InventoryCloseEvent event) { if (!(event.getInventory().getHolder() instanceof FishingBagHolder)) @@ -92,6 +112,12 @@ public class BagManagerImpl implements BagManager, Listener { plugin.getStorageManager().saveUserData(offlineUser, true); } + /** + * Handles InventoryClickEvent to prevent certain actions on the Fishing Bag inventory. + * This method cancels the event if specific conditions are met to restrict certain item interactions. + * + * @param event The InventoryClickEvent triggered when an item is clicked in an inventory. + */ @EventHandler public void onInvClick(InventoryClickEvent event) { if (event.isCancelled()) @@ -106,18 +132,28 @@ public class BagManagerImpl implements BagManager, Listener { return; if (CFConfig.bagWhiteListItems.contains(clickedItem.getType())) return; - String id = plugin.getItemManager().getAnyItemID(clickedItem); - if (plugin.getEffectManager().getEffect("rod", id) != null) - return; - if (plugin.getEffectManager().getEffect("bait", id) != null) - return; - if (plugin.getEffectManager().getEffect("util", id) != null) + String id = plugin.getItemManager().getAnyPluginItemID(clickedItem); + EffectManager effectManager = plugin.getEffectManager(); + if (effectManager.hasEffectCarrier("rod", id) + || effectManager.hasEffectCarrier("bait", id) + || effectManager.hasEffectCarrier("util", id) + || effectManager.hasEffectCarrier("hook", id) + ) { return; + } if (CFConfig.bagStoreLoots && plugin.getLootManager().getLoot(id) != null) return; event.setCancelled(true); } + /** + * Event handler for the PlayerQuitEvent. + * This method is triggered when a player quits the server. + * It checks if the player was in the process of editing an offline player's bag inventory, + * and if so, saves the offline player's data if necessary. + * + * @param event The PlayerQuitEvent triggered when a player quits. + */ @EventHandler public void onQuit(PlayerQuitEvent event) { OfflineUser offlineUser = tempEditMap.remove(event.getPlayer().getUniqueId()); diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java index 2f9694ad..eb300f36 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java @@ -50,6 +50,7 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.util.*; @@ -74,17 +75,32 @@ public class BlockManagerImpl implements BlockManager, Listener { this.registerInbuiltProperties(); } + /** + * Event handler for the EntityChangeBlockEvent. + * This method is triggered when an entity changes a block, typically when a block falls or lands. + */ @EventHandler public void onBlockLands(EntityChangeBlockEvent event) { - if (event.isCancelled()) return; + if (event.isCancelled()) + return; + + // Retrieve a custom string value stored in the entity's persistent data container. String temp = event.getEntity().getPersistentDataContainer().get( Objects.requireNonNull(NamespacedKey.fromString("block", CustomFishingPlugin.get())), PersistentDataType.STRING ); + + // If the custom string value is not present, return without further action. if (temp == null) return; + + // "BLOCK;PLAYER" String[] split = temp.split(";"); + + // If no BlockConfig is found for the specified key, return without further action. BlockConfig blockConfig = blockConfigMap.get(split[0]); if (blockConfig == null) return; + + // If the player is not online or not found, remove the entity and set the block to air Player player = Bukkit.getPlayer(split[1]); if (player == null) { event.getEntity().remove(); @@ -92,6 +108,8 @@ public class BlockManagerImpl implements BlockManager, Listener { return; } Location location = event.getBlock().getLocation(); + + // Apply block state modifiers from the BlockConfig to the block 1 tick later. plugin.getScheduler().runTaskSyncLater(() -> { BlockState state = location.getBlock().getState(); for (BlockStateModifier modifier : blockConfig.getStateModifierList()) { @@ -100,23 +118,40 @@ public class BlockManagerImpl implements BlockManager, Listener { }, location, 50, TimeUnit.MILLISECONDS); } + /** + * Registers a BlockLibrary instance. + * This method associates a BlockLibrary with its unique identification and adds it to the registry. + * + * @param blockLibrary The BlockLibrary instance to register. + * @return True if the registration was successful (the identification is not already registered), false otherwise. + */ @Override - public boolean registerBlockLibrary(BlockLibrary library) { - if (this.blockLibraryMap.containsKey(library.identification())) return false; - this.blockLibraryMap.put(library.identification(), library); + public boolean registerBlockLibrary(BlockLibrary blockLibrary) { + if (this.blockLibraryMap.containsKey(blockLibrary.identification())) return false; + this.blockLibraryMap.put(blockLibrary.identification(), blockLibrary); return true; } + /** + * Unregisters a BlockLibrary instance by its identification. + * This method removes a BlockLibrary from the registry based on its unique identification. + * + * @param identification The unique identification of the BlockLibrary to unregister. + * @return True if the BlockLibrary was successfully unregistered, false if it was not found. + */ @Override - public boolean unregisterBlockLibrary(BlockLibrary library) { - return unregisterBlockLibrary(library.identification()); - } - - @Override - public boolean unregisterBlockLibrary(String library) { - return blockLibraryMap.remove(library) != null; + public boolean unregisterBlockLibrary(String identification) { + return blockLibraryMap.remove(identification) != null; } + /** + * Registers a BlockDataModifierBuilder for a specific type. + * This method associates a BlockDataModifierBuilder with its type and adds it to the registry. + * + * @param type The type of the BlockDataModifierBuilder to register. + * @param builder The BlockDataModifierBuilder instance to register. + * @return True if the registration was successful (the type is not already registered), false otherwise. + */ @Override public boolean registerBlockDataModifierBuilder(String type, BlockDataModifierBuilder builder) { if (dataBuilderMap.containsKey(type)) return false; @@ -124,6 +159,14 @@ public class BlockManagerImpl implements BlockManager, Listener { return true; } + /** + * Registers a BlockStateModifierBuilder for a specific type. + * This method associates a BlockStateModifierBuilder with its type and adds it to the registry. + * + * @param type The type of the BlockStateModifierBuilder to register. + * @param builder The BlockStateModifierBuilder instance to register. + * @return True if the registration was successful (the type is not already registered), false otherwise. + */ @Override public boolean registerBlockStateModifierBuilder(String type, BlockStateModifierBuilder builder) { if (stateBuilderMap.containsKey(type)) return false; @@ -131,6 +174,28 @@ public class BlockManagerImpl implements BlockManager, Listener { return true; } + /** + * Unregisters a BlockDataModifierBuilder with the specified type. + * + * @param type The type of the BlockDataModifierBuilder to unregister. + * @return True if the BlockDataModifierBuilder was successfully unregistered, false otherwise. + */ + @Override + public boolean unregisterBlockDataModifierBuilder(String type) { + return dataBuilderMap.remove(type) != null; + } + + /** + * Unregisters a BlockStateModifierBuilder with the specified type. + * + * @param type The type of the BlockStateModifierBuilder to unregister. + * @return True if the BlockStateModifierBuilder was successfully unregistered, false otherwise. + */ + @Override + public boolean unregisterBlockStateModifierBuilder(String type) { + return stateBuilderMap.remove(type) != null; + } + public void load() { this.loadConfig(); Bukkit.getPluginManager().registerEvents(this, plugin); @@ -162,6 +227,10 @@ public class BlockManagerImpl implements BlockManager, Listener { this.blockLibraryMap.clear(); } + /** + * Loads configuration files from the plugin's data folder and processes them. + * Configuration files are organized by type (e.g., "block"). + */ @SuppressWarnings("DuplicatedCode") private void loadConfig() { Deque fileDeque = new ArrayDeque<>(); @@ -187,10 +256,17 @@ public class BlockManagerImpl implements BlockManager, Listener { } } + /** + * Loads configuration data from a single YAML file and processes it to create BlockConfig instances. + * + * @param file The YAML file to load and process. + */ private void loadSingleFile(File file) { YamlConfiguration config = YamlConfiguration.loadConfiguration(file); for (Map.Entry entry : config.getValues(false).entrySet()) { if (entry.getValue() instanceof ConfigurationSection section) { + + // Check if the "block" is null and log a warning if so. String blockID = section.getString("block"); if (blockID == null) { LogUtils.warn("Block can't be null. File:" + file.getAbsolutePath() + "; Section:" + section.getCurrentPath()); @@ -198,6 +274,8 @@ public class BlockManagerImpl implements BlockManager, Listener { } List dataModifiers = new ArrayList<>(); List stateModifiers = new ArrayList<>(); + + // If a "properties" section exists, process its entries. ConfigurationSection property = section.getConfigurationSection("properties"); if (property != null) { for (Map.Entry innerEntry : property.getValues(false).entrySet()) { @@ -212,6 +290,8 @@ public class BlockManagerImpl implements BlockManager, Listener { } } } + + // Create a BlockConfig instance with the processed data and add it to the blockConfigMap. BlockConfig blockConfig = new BlockConfig.Builder() .blockID(blockID) .persist(false) @@ -225,6 +305,15 @@ public class BlockManagerImpl implements BlockManager, Listener { } } + /** + * Summons a falling block at a specified location based on the provided loot. + * This method spawns a falling block at the given hookLocation with specific properties determined by the loot. + * + * @param player The player who triggered the action. + * @param hookLocation The location where the hook is positioned. + * @param playerLocation The location of the player. + * @param loot The loot to be associated with the summoned block. + */ @Override public void summonBlock(Player player, Location hookLocation, Location playerLocation, Loot loot) { BlockConfig config = blockConfigMap.get(loot.getID()); @@ -253,8 +342,17 @@ public class BlockManagerImpl implements BlockManager, Listener { fallingBlock.setVelocity(vector); } + /** + * Retrieves the block ID associated with a given Block instance using block detection order. + * This method iterates through the configured block detection order to find the block's ID + * by checking different BlockLibrary instances in the specified order. + * + * @param block The Block instance for which to retrieve the block ID. + * @return The block ID + */ @Override - public String getAnyBlockID(Block block) { + @NotNull + public String getAnyPluginBlockID(Block block) { for (String plugin : CFConfig.blockDetectOrder) { BlockLibrary blockLibrary = blockLibraryMap.get(plugin); if (blockLibrary != null) { @@ -264,8 +362,8 @@ public class BlockManagerImpl implements BlockManager, Listener { } } } - // should not reach this because vanilla library would always work - return null; + // Should not reach this because vanilla library would always work + return "AIR"; } private void registerDirectional() { @@ -389,6 +487,13 @@ public class BlockManagerImpl implements BlockManager, Listener { }); } + /** + * Sets items in the BLOCK's inventory based on chance and configuration. + * + * @param tempChanceList A list of tuples containing chance, item ID, and quantity range for each item. + * @param player The inventory items are being set. + * @param inventory The inventory where the items will be placed. + */ private void setInventoryItems( ArrayList>> tempChanceList, Player player, @@ -400,11 +505,11 @@ public class BlockManagerImpl implements BlockManager, Listener { } Collections.shuffle(unused); for (Tuple> tuple : tempChanceList) { - ItemStack itemStack = plugin.getItemManager().buildAnyItemByID(player, tuple.getMid()); + ItemStack itemStack = plugin.getItemManager().buildAnyPluginItemByID(player, tuple.getMid()); itemStack.setAmount(ThreadLocalRandom.current().nextInt(tuple.getRight().left(), tuple.getRight().right() + 1)); if (tuple.getLeft() > Math.random()) { inventory.setItem(unused.pop(), itemStack); } } } -} +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/Competition.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/Competition.java index 58e27c18..61405c71 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/Competition.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/Competition.java @@ -35,10 +35,12 @@ import net.momirealms.customfishing.setting.CFLocale; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import java.time.Instant; import java.util.HashMap; import java.util.Iterator; +import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -62,13 +64,19 @@ public class Competition implements FishingCompetition { if (CFConfig.redisRanking) this.ranking = new RedisRankingImpl(); else this.ranking = new LocalRankingImpl(); this.publicPlaceholders = new ConcurrentHashMap<>(); - this.publicPlaceholders.put("{goal}", CustomFishingPlugin.get().getCompetitionManager().getCompetitionLocale(goal)); + this.publicPlaceholders.put("{goal}", CustomFishingPlugin.get().getCompetitionManager().getCompetitionGoalLocale(goal)); } + /** + * Starts the fishing competition, initializing its settings and actions. + * This method sets the initial progress, remaining time, start time, and updates public placeholders. + * It also arranges timer tasks for competition timing and initializes boss bar and action bar managers if configured. + * Additionally, it triggers the start actions defined in the competition's configuration. + */ @Override public void start() { this.progress = 1; - this.remainingTime = config.getDuration(); + this.remainingTime = config.getDurationInSeconds(); this.startTime = Instant.now().getEpochSecond(); this.updatePublicPlaceholders(); @@ -92,6 +100,11 @@ public class Competition implements FishingCompetition { } } + /** + * Arranges the timer task for the fishing competition. + * This method schedules a recurring task that updates the competition's remaining time and public placeholders. + * If the remaining time reaches zero, the competition is ended. + */ private void arrangeTimerTask() { this.competitionTimerTask = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(() -> { if (decreaseTime()) { @@ -102,6 +115,12 @@ public class Competition implements FishingCompetition { }, 1, 1, TimeUnit.SECONDS); } + /** + * Update public placeholders for the fishing competition. + * This method updates placeholders representing player rankings, remaining time, and score in public messages. + * Placeholders for player rankings include {1_player}, {1_score}, {2_player}, {2_score}, and so on. + * The placeholders for time include {hour}, {minute}, {second}, and {seconds}. + */ private void updatePublicPlaceholders() { for (int i = 1; i < CFConfig.placeholderLimit + 1; i++) { int finalI = i; @@ -119,6 +138,11 @@ public class Competition implements FishingCompetition { publicPlaceholders.put("{seconds}", String.valueOf(remainingTime)); } + /** + * Stop the fishing competition. + * This method cancels the competition timer task, unloads boss bars and action bars, clears the ranking, + * and sets the remaining time to zero. + */ @Override public void stop() { if (!competitionTimerTask.isCancelled()) this.competitionTimerTask.cancel(); @@ -128,6 +152,11 @@ public class Competition implements FishingCompetition { this.remainingTime = 0; } + /** + * End the fishing competition. + * This method marks the competition as ended, cancels sub-tasks such as timers and bar management, + * gives prizes to top participants and participation rewards, performs end actions, and clears the ranking. + */ @Override public void end() { // mark it as ended @@ -182,19 +211,36 @@ public class Competition implements FishingCompetition { CustomFishingPlugin.get().getScheduler().runTaskAsyncLater(this.ranking::clear, 1500, TimeUnit.MILLISECONDS); } + /** + * Check if the fishing competition is ongoing. + * + * @return {@code true} if the competition is still ongoing, {@code false} if it has ended. + */ @Override public boolean isOnGoing() { return remainingTime > 0; } + /** + * Decreases the remaining time for the fishing competition and updates the progress. + * + * @return {@code true} if the remaining time becomes zero or less, indicating the competition has ended. + */ private boolean decreaseTime() { long current = Instant.now().getEpochSecond(); - int duration = config.getDuration(); + int duration = config.getDurationInSeconds(); remainingTime = duration - (current - startTime); progress = (float) remainingTime / duration; return remainingTime <= 0; } + /** + * Refreshes the data for a player in the fishing competition, including updating their score and triggering + * actions if it's their first time joining the competition. + * + * @param player The player whose data needs to be refreshed. + * @param score The player's current score in the competition. + */ @Override public void refreshData(Player player, double score) { // if player join for the first time, trigger join actions @@ -224,46 +270,97 @@ public class Competition implements FishingCompetition { } } + /** + * Checks if a player has joined the fishing competition based on their name. + * + * @param player The player to check for participation. + * @return {@code true} if the player has joined the competition; {@code false} otherwise. + */ @Override public boolean hasPlayerJoined(OfflinePlayer player) { return ranking.getPlayerRank(player.getName()) != -1; } + /** + * Gets the progress of the fishing competition as a float value (0~1). + * + * @return The progress of the fishing competition as a float. + */ @Override public float getProgress() { return progress; } + /** + * Gets the remaining time in seconds for the fishing competition. + * + * @return The remaining time in seconds. + */ @Override public long getRemainingTime() { return remainingTime; } + /** + * Gets the start time of the fishing competition. + * + * @return The start time of the fishing competition. + */ @Override public long getStartTime() { return startTime; } + /** + * Gets the configuration of the fishing competition. + * + * @return The configuration of the fishing competition. + */ + @NotNull @Override public CompetitionConfig getConfig() { return config; } + /** + * Gets the goal of the fishing competition. + * + * @return The goal of the fishing competition. + */ + @NotNull @Override public CompetitionGoal getGoal() { return goal; } + /** + * Gets the ranking data for the fishing competition. + * + * @return The ranking data for the fishing competition. + */ + @NotNull @Override public Ranking getRanking() { return ranking; } + /** + * Gets the cached placeholders for the fishing competition. + * + * @return A ConcurrentHashMap containing cached placeholders. + */ + @NotNull @Override - public ConcurrentHashMap getCachedPlaceholders() { + public Map getCachedPlaceholders() { return publicPlaceholders; } + /** + * Gets a specific cached placeholder value by its key. + * + * @param papi The key of the cached placeholder. + * @return The cached placeholder value as a string, or null if not found. + */ @Override public String getCachedPlaceholder(String papi) { return publicPlaceholders.get(papi); diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java index 17d9f1aa..c95f13de 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java @@ -25,7 +25,6 @@ import net.momirealms.customfishing.api.mechanic.competition.*; import net.momirealms.customfishing.api.mechanic.condition.Condition; import net.momirealms.customfishing.api.scheduler.CancellableTask; import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.setting.CFConfig; import net.momirealms.customfishing.setting.CFLocale; import net.momirealms.customfishing.storage.method.database.nosql.RedisManager; import org.bukkit.Bukkit; @@ -33,12 +32,12 @@ import org.bukkit.boss.BarColor; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.time.LocalDateTime; import java.util.*; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; public class CompetitionManagerImpl implements CompetitionManager { @@ -84,8 +83,14 @@ public class CompetitionManagerImpl implements CompetitionManager { currentCompetition.stop(); } + /** + * Retrieves a set of all competition names. + * + * @return A set of competition names. + */ + @NotNull @Override - public Set getAllCompetitions() { + public Set getAllCompetitionKeys() { return commandConfigMap.keySet(); } @@ -202,8 +207,15 @@ public class CompetitionManagerImpl implements CompetitionManager { } } + /** + * Retrieves the localization key for a given competition goal. + * + * @param goal The competition goal to retrieve the localization key for. + * @return The localization key for the specified competition goal. + */ + @NotNull @Override - public String getCompetitionLocale(CompetitionGoal goal) { + public String getCompetitionGoalLocale(CompetitionGoal goal) { switch (goal) { case MAX_SIZE -> { return CFLocale.MSG_Max_Size; @@ -221,16 +233,29 @@ public class CompetitionManagerImpl implements CompetitionManager { return ""; } + /** + * Starts a competition with the specified name, allowing for the option to force start it or apply it to the entire server. + * + * @param competition The name of the competition to start. + * @param force Whether to force start the competition even if amount of the online players is lower than the requirement + * @param allServer Whether to apply the competition to the servers that connected to Redis. + * @return {@code true} if the competition was started successfully, {@code false} otherwise. + */ @Override - public void startCompetition(String competition, boolean force, boolean allServer) { + public boolean startCompetition(String competition, boolean force, boolean allServer) { CompetitionConfig config = commandConfigMap.get(competition); if (config == null) { LogUtils.warn("Competition " + competition + " doesn't exist."); - return; + return false; } - startCompetition(config, force, allServer); + return startCompetition(config, force, allServer); } + /** + * Gets the ongoing fishing competition, if one is currently in progress. + * + * @return The ongoing fishing competition, or null if there is none. + */ @Override @Nullable public FishingCompetition getOnGoingCompetition() { @@ -238,26 +263,36 @@ public class CompetitionManagerImpl implements CompetitionManager { return currentCompetition.isOnGoing() ? currentCompetition : null; } + /** + * Starts a competition using the specified configuration. + * + * @param config The configuration of the competition to start. + * @param force Whether to force the start of the competition. + * @param allServer Whether the competition should start across all servers in the network. + * @return True if the competition was started successfully, false otherwise. + */ @Override - public void startCompetition(CompetitionConfig config, boolean force, boolean allServer) { - if (!force) - this.getPlayerCount().thenAccept(count -> { - if (count < config.getMinPlayers()) { - var actions = config.getSkipActions(); - if (actions != null) - for (Player player : Bukkit.getOnlinePlayers()) { - for (Action action : actions) { - action.trigger(new Condition(player)); - } + public boolean startCompetition(CompetitionConfig config, boolean force, boolean allServer) { + if (!force) { + int players = Bukkit.getOnlinePlayers().size(); + if (players < config.getMinPlayersToStart()) { + var actions = config.getSkipActions(); + if (actions != null) + for (Player player : Bukkit.getOnlinePlayers()) { + for (Action action : actions) { + action.trigger(new Condition(player)); } - return; - } - start(config); - }); - else if (!allServer) { + } + return false; + } start(config); + return true; + } else if (!allServer) { + start(config); + return true; } else { RedisManager.getInstance().sendRedisMessage("cf_competition", "start;" + config.getKey()); + return true; } } @@ -274,20 +309,22 @@ public class CompetitionManagerImpl implements CompetitionManager { } } + /** + * Gets the number of seconds until the next competition. + * + * @return The number of seconds until the next competition. + */ @Override public int getNextCompetitionSeconds() { return nextCompetitionSeconds; } - @Override - public CompletableFuture getPlayerCount() { - if (!CFConfig.redisRanking) { - return CompletableFuture.completedFuture(Bukkit.getOnlinePlayers().size()); - } else { - return plugin.getStorageManager().getRedisPlayerCount(); - } - } - + /** + * Retrieves the configuration for a competition based on its key. + * + * @param key The key of the competition configuration to retrieve. + * @return The {@link CompetitionConfig} for the specified key, or {@code null} if no configuration exists with that key. + */ @Nullable @Override public CompetitionConfig getConfig(String key) { diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/effect/EffectManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/effect/EffectManagerImpl.java index 23abeac5..f217d639 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/effect/EffectManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/effect/EffectManagerImpl.java @@ -21,7 +21,6 @@ import net.momirealms.customfishing.api.CustomFishingPlugin; import net.momirealms.customfishing.api.common.Key; import net.momirealms.customfishing.api.common.Pair; import net.momirealms.customfishing.api.manager.EffectManager; -import net.momirealms.customfishing.api.mechanic.effect.Effect; import net.momirealms.customfishing.api.mechanic.effect.EffectCarrier; import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; import net.momirealms.customfishing.api.mechanic.effect.FishingEffect; @@ -31,6 +30,7 @@ import net.momirealms.customfishing.api.util.LogUtils; import net.momirealms.customfishing.util.ConfigUtils; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; @@ -51,24 +51,61 @@ public class EffectManagerImpl implements EffectManager { this.effectMap.clear(); } + /** + * Registers an EffectCarrier with a unique Key. + * + * @param key The unique Key associated with the EffectCarrier. + * @param effect The EffectCarrier to be registered. + * @return True if the registration was successful, false if the Key already exists. + */ @Override - public boolean registerEffectItem(Key key, EffectCarrier effect) { + public boolean registerEffectCarrier(Key key, EffectCarrier effect) { if (effectMap.containsKey(key)) return false; this.effectMap.put(key, effect); return true; } + /** + * Unregisters an EffectCarrier associated with the specified Key. + * + * @param key The unique Key of the EffectCarrier to unregister. + * @return True if the EffectCarrier was successfully unregistered, false if the Key does not exist. + */ @Override - public boolean unregisterEffectItem(Key key) { + public boolean unregisterEffectCarrier(Key key) { return this.effectMap.remove(key) != null; } + /** + * Checks if an EffectCarrier with the specified namespace and id exists. + * + * @param namespace The namespace of the EffectCarrier. + * @param id The unique identifier of the EffectCarrier. + * @return True if an EffectCarrier with the given namespace and id exists, false otherwise. + */ + @Override + public boolean hasEffectCarrier(String namespace, String id) { + return effectMap.containsKey(Key.of(namespace, id)); + } + + /** + * Retrieves an EffectCarrier with the specified namespace and id. + * + * @param namespace The namespace of the EffectCarrier. + * @param id The unique identifier of the EffectCarrier. + * @return The EffectCarrier with the given namespace and id, or null if it doesn't exist. + */ @Nullable @Override - public EffectCarrier getEffect(String namespace, String id) { + public EffectCarrier getEffectCarrier(String namespace, String id) { return effectMap.get(Key.of(namespace, id)); } + /** + * Loads EffectCarrier configurations from YAML files in different content folders. + * EffectCarrier configurations are organized by type (rod, bait, enchant, util, totem, hook) in separate folders. + * Each YAML file within these folders is processed to populate the effectMap. + */ @SuppressWarnings("DuplicatedCode") public void load() { Deque fileDeque = new ArrayDeque<>(); @@ -94,20 +131,35 @@ public class EffectManagerImpl implements EffectManager { } } + /** + * Loads EffectCarrier configurations from a YAML file and populates the effectMap. + * + * @param file The YAML file to load configurations from. + * @param namespace The namespace to use when creating keys for EffectCarriers. + */ private void loadSingleFile(File file, String namespace) { YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file); for (Map.Entry entry : yaml.getValues(false).entrySet()) { String value = entry.getKey(); if (entry.getValue() instanceof ConfigurationSection section) { Key key = Key.of(namespace, value); - EffectCarrier item = getEffectItemFromSection(key, section); + EffectCarrier item = getEffectCarrierFromSection(key, section); if (item != null) effectMap.put(key, item); } } } - private EffectCarrier getEffectItemFromSection(Key key, ConfigurationSection section) { + /** + * Parses a ConfigurationSection to create an EffectCarrier based on the specified key and configuration. + * + * @param key The key that uniquely identifies the EffectCarrier. + * @param section The ConfigurationSection containing the EffectCarrier configuration. + * @return An EffectCarrier instance based on the key and configuration, or null if the section is null. + */ + @Override + @Nullable + public EffectCarrier getEffectCarrierFromSection(Key key, ConfigurationSection section) { if (section == null) return null; return new EffectCarrier.Builder() .key(key) @@ -117,23 +169,6 @@ public class EffectManagerImpl implements EffectManager { .build(); } - public Effect getEffectFromSection(ConfigurationSection section) { - if (section == null) return getInitialEffect(); - return new FishingEffect.Builder() - .addWeightModifier(ConfigUtils.getModifiers(section.getStringList("weight-single"))) - .addWeightModifier(getGroupModifiers(section.getStringList("weight-group"))) - .addWeightModifierIgnored(ConfigUtils.getModifiers(section.getStringList("weight-single-ignore-condition"))) - .addWeightModifierIgnored(getGroupModifiers(section.getStringList("weight-group-ignore-condition"))) - .timeModifier(section.getDouble("hook-time", 1)) - .difficultyModifier(section.getDouble("difficulty", 0)) - .multipleLootChance(section.getDouble("multiple-loot", 0)) - .lavaFishing(section.getBoolean("lava-fishing", false)) - .scoreMultiplier(section.getDouble("score-bonus", 1)) - .sizeMultiplier(section.getDouble("size-bonus", 1)) - .gameTimeModifier(section.getDouble("game-time", 0)) - .build(); - } - public void unload() { HashMap temp = new HashMap<>(effectMap); effectMap.clear(); @@ -144,11 +179,23 @@ public class EffectManagerImpl implements EffectManager { } } + /** + * Retrieves the initial FishingEffect that represents no special effects. + * + * @return The initial FishingEffect. + */ + @NotNull @Override public FishingEffect getInitialEffect() { return new FishingEffect.Builder().build(); } + /** + * Retrieves a list of modifiers based on specified loot groups. + * + * @param modList A list of strings containing group modifiers in the format "group:modifier". + * @return A list of pairs where each pair represents a loot item and its associated modifier. + */ private List> getGroupModifiers(List modList) { List> result = new ArrayList<>(); for (String group : modList) { @@ -166,6 +213,14 @@ public class EffectManagerImpl implements EffectManager { return result; } + /** + * Parses a ConfigurationSection to retrieve an array of EffectModifiers. + * + * @param section The ConfigurationSection to parse. + * @return An array of EffectModifiers based on the values found in the section. + */ + @NotNull + @Override public EffectModifier[] getEffectModifiers(ConfigurationSection section) { if (section == null) return new EffectModifier[0]; ArrayList modifiers = new ArrayList<>(); @@ -179,6 +234,14 @@ public class EffectManagerImpl implements EffectManager { return modifiers.toArray(new EffectModifier[0]); } + /** + * Parses a ConfigurationSection to create an EffectModifier based on the specified type and configuration. + * + * @param section The ConfigurationSection containing the effect modifier configuration. + * @return An EffectModifier instance based on the type and configuration. + */ + @Override + @Nullable public EffectModifier getEffectModifier(ConfigurationSection section) { String type = section.getString("type"); if (type == null) return null; diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/entity/EntityManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/entity/EntityManagerImpl.java index e7baee0b..af97b0de 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/entity/EntityManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/entity/EntityManagerImpl.java @@ -60,6 +60,12 @@ public class EntityManagerImpl implements EntityManager { } } + /** + * Registers an entity library for use in the plugin. + * + * @param entityLibrary The entity library to register. + * @return {@code true} if the entity library was successfully registered, {@code false} if it already exists. + */ @Override public boolean registerEntityLibrary(EntityLibrary entityLibrary) { if (entityLibraryMap.containsKey(entityLibrary.identification())) return false; @@ -67,14 +73,15 @@ public class EntityManagerImpl implements EntityManager { return true; } + /** + * Unregisters an entity library by its identification key. + * + * @param identification The identification key of the entity library to unregister. + * @return {@code true} if the entity library was successfully unregistered, {@code false} if it does not exist. + */ @Override - public boolean unregisterEntityLibrary(String lib) { - return entityLibraryMap.remove(lib) != null; - } - - @Override - public boolean unregisterEntityLibrary(EntityLibrary entityLibrary) { - return unregisterEntityLibrary(entityLibrary.identification()); + public boolean unregisterEntityLibrary(String identification) { + return entityLibraryMap.remove(identification) != null; } @SuppressWarnings("DuplicatedCode") @@ -134,6 +141,13 @@ public class EntityManagerImpl implements EntityManager { this.entityLibraryMap.clear(); } + /** + * Summons an entity based on the given loot configuration to a specified location. + * + * @param hookLocation The location where the entity will be summoned, typically where the fishing hook is. + * @param playerLocation The location of the player who triggered the entity summoning. + * @param loot The loot configuration that defines the entity to be summoned. + */ @Override public void summonEntity(Location hookLocation, Location playerLocation, Loot loot) { EntityConfig config = entityConfigMap.get(loot.getID()); diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/BaitAnimationTask.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/BaitAnimationTask.java index 8b7208cf..ad25d762 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/BaitAnimationTask.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/BaitAnimationTask.java @@ -54,7 +54,7 @@ public class BaitAnimationTask implements Runnable { ) { cancelAnimation(); } else { - CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getVelocity(entityID, fishHook.getVelocity())); + CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getVelocityPacket(entityID, fishHook.getVelocity())); CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getTpPacket(entityID, fishHook.getLocation())); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java index 914605a2..53e693e2 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java @@ -43,7 +43,6 @@ import net.momirealms.customfishing.api.mechanic.game.GameSettings; import net.momirealms.customfishing.api.mechanic.game.GamingPlayer; import net.momirealms.customfishing.api.mechanic.loot.Loot; import net.momirealms.customfishing.api.mechanic.loot.LootType; -import net.momirealms.customfishing.api.mechanic.loot.WeightModifier; import net.momirealms.customfishing.api.util.LogUtils; import net.momirealms.customfishing.api.util.WeightUtils; import net.momirealms.customfishing.mechanic.requirement.RequirementManagerImpl; @@ -55,7 +54,6 @@ import org.bukkit.event.*; import org.bukkit.event.player.*; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -142,7 +140,9 @@ public class FishingManagerImpl implements Listener, FishingManager { @EventHandler public void onQuit(PlayerQuitEvent event) { + final Player player = event.getPlayer(); this.removeHook(event.getPlayer().getUniqueId()); + this.removeTempFishingState(player); } @EventHandler @@ -189,6 +189,12 @@ public class FishingManagerImpl implements Listener, FishingManager { } } + /** + * Removes a fishing hook entity associated with a given UUID. + * + * @param uuid The UUID of the fishing hook entity to be removed. + * @return {@code true} if the fishing hook was successfully removed, {@code false} otherwise. + */ @Override public boolean removeHook(UUID uuid) { FishHook hook = hookCacheMap.remove(uuid); @@ -200,11 +206,32 @@ public class FishingManagerImpl implements Listener, FishingManager { } } + /** + * Retrieves a FishHook object associated with the provided player's UUID + * + * @param uuid The UUID of the player + * @return fishhook entity, null if not exists + */ @Override - public Optional getHook(UUID uuid) { - return Optional.ofNullable(hookCacheMap.get(uuid)); + @Nullable + public FishHook getHook(UUID uuid) { + FishHook fishHook = hookCacheMap.get(uuid); + if (fishHook != null) { + if (!fishHook.isValid()) { + hookCacheMap.remove(uuid); + return null; + } else { + return fishHook; + } + } + return null; } + /** + * Selects the appropriate fishing state based on the provided PlayerFishEvent and triggers the corresponding action. + * + * @param event The PlayerFishEvent that represents the fishing action. + */ public void selectState(PlayerFishEvent event) { if (event.isCancelled()) return; switch (event.getState()) { @@ -229,7 +256,7 @@ public class FishingManagerImpl implements Listener, FishingManager { if (compound != null && compound.hasTag("max_dur")) { event.setCancelled(true); hook.remove(); - ItemUtils.loseDurability(itemStack, 2, true); + ItemUtils.decreaseDurability(itemStack, 2, true); } } } @@ -243,9 +270,8 @@ public class FishingManagerImpl implements Listener, FishingManager { return; } // Check mechanic requirements - if (!RequirementManager.isRequirementsMet( - RequirementManagerImpl.mechanicRequirements, - fishingPreparation + if (!RequirementManager.isRequirementMet( + fishingPreparation, RequirementManagerImpl.mechanicRequirements )) { return; } @@ -330,7 +356,7 @@ public class FishingManagerImpl implements Listener, FishingManager { if (nbtCompound != null && nbtCompound.hasTag("max_dur")) { event.getHook().remove(); event.setCancelled(true); - ItemUtils.loseDurability(itemStack, 5, true); + ItemUtils.decreaseDurability(itemStack, 5, true); } } } @@ -444,11 +470,21 @@ public class FishingManagerImpl implements Listener, FishingManager { } } + /** + * Removes the temporary fishing state associated with a player. + * + * @param player The player whose temporary fishing state should be removed. + */ @Override - public void removeTempFishingState(Player player) { - this.tempFishingStateMap.remove(player.getUniqueId()); + public TempFishingState removeTempFishingState(Player player) { + return this.tempFishingStateMap.remove(player.getUniqueId()); } + /** + * Processes the game result for a gaming player + * + * @param gamingPlayer The gaming player whose game result should be processed. + */ @Override public void processGameResult(GamingPlayer gamingPlayer) { final Player player = gamingPlayer.getPlayer(); @@ -458,7 +494,7 @@ public class FishingManagerImpl implements Listener, FishingManager { LogUtils.warn("Unexpected situation: Can't get player's fish hook when processing game results."); return; } - TempFishingState tempFishingState = tempFishingStateMap.remove(uuid); + TempFishingState tempFishingState = removeTempFishingState(player); if (tempFishingState == null) { LogUtils.warn("Unexpected situation: Can't get player's fishing state when processing game results."); return; @@ -479,8 +515,8 @@ public class FishingManagerImpl implements Listener, FishingManager { if (damageEvent.isCancelled()) { break outer; } - ItemUtils.reduceHookDurability(rod, false); - ItemUtils.loseDurability(rod, 1, true); + ItemUtils.decreaseHookDurability(rod, 1, false); + ItemUtils.decreaseDurability(rod, 1, true); } fishHook.remove(); @@ -520,7 +556,7 @@ public class FishingManagerImpl implements Listener, FishingManager { loot.triggerActions(ActionTrigger.FAILURE, fishingPreparation); fishingPreparation.triggerActions(ActionTrigger.FAILURE); - ItemUtils.reduceHookDurability(fishingPreparation.getRodItemStack(), true); + ItemUtils.decreaseHookDurability(fishingPreparation.getRodItemStack(), 1, true); } public void success(TempFishingState state, FishHook hook) { @@ -565,7 +601,7 @@ public class FishingManagerImpl implements Listener, FishingManager { } } else { for (int i = 0; i < amount; i++) { - plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), loot, fishingPreparation.getArgs()); + plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), loot.getID(), fishingPreparation.getArgs()); doSuccessActions(loot, effect, fishingPreparation, player); } } @@ -629,67 +665,56 @@ public class FishingManagerImpl implements Listener, FishingManager { ).ifPresent(it -> it.addLootAmount(loot, fishingPreparation, 1)); } - @Override - public Collection getPossibleLootKeys (Condition condition) { - return plugin.getRequirementManager().getLootWithWeight(condition).keySet(); - } - - @NotNull - @Override - public Map getPossibleLootKeysWithWeight(Effect initialEffect, Condition condition) { - Map lootWithWeight = plugin.getRequirementManager().getLootWithWeight(condition); - - Player player = condition.getPlayer(); - for (Pair pair : initialEffect.getWeightModifier()) { - Double previous = lootWithWeight.get(pair.left()); - if (previous != null) - lootWithWeight.put(pair.left(), pair.right().modify(player, previous)); - } - for (Pair pair : initialEffect.getWeightModifierIgnored()) { - double previous = lootWithWeight.getOrDefault(pair.left(), 0d); - lootWithWeight.put(pair.left(), pair.right().modify(player, previous)); - } - return lootWithWeight; - } - - @Override - @Nullable - public Loot getNextLoot(Effect initialEffect, Condition condition) { - String key = WeightUtils.getRandom(getPossibleLootKeysWithWeight(initialEffect, condition)); - Loot loot = plugin.getLootManager().getLoot(key); - if (loot == null) { - LogUtils.warn(String.format("Loot %s doesn't exist!", key)); - return null; - } - return loot; - } - + /** + * Starts a fishing game for the specified player with the given condition and effect. + * + * @param player The player starting the fishing game. + * @param condition The condition used to determine the game. + * @param effect The effect applied to the game. + */ @Override public void startFishingGame(Player player, Condition condition, Effect effect) { - Map gameWithWeight = plugin.getRequirementManager().getGameWithWeight(condition); + Map gameWithWeight = plugin.getGameManager().getGameWithWeight(condition); plugin.debug(gameWithWeight.toString()); String random = WeightUtils.getRandom(gameWithWeight); - Optional> gamePair = plugin.getGameManager().getGame(random); - if (gamePair.isEmpty()) { - LogUtils.warn(String.format("Game %s doesn't exist!", random)); + Pair gamePair = plugin.getGameManager().getGameInstance(random); + if (random == null) { + LogUtils.warn("No game is available for player:" + player.getName() + " location:" + condition.getLocation()); + return; + } + if (gamePair == null) { + LogUtils.warn(String.format("Game %s doesn't exist.", random)); return; } plugin.debug("Game: " + random); - startFishingGame(player, Objects.requireNonNull(gamePair.get().left().getGameSetting(effect)), gamePair.get().right()); + startFishingGame(player, Objects.requireNonNull(gamePair.left().getGameSetting(effect)), gamePair.right()); } + /** + * Starts a fishing game for the specified player with the given settings and game instance. + * + * @param player The player starting the fishing game. + * @param settings The game settings for the fishing game. + * @param gameInstance The instance of the fishing game to start. + */ @Override public void startFishingGame(Player player, GameSettings settings, GameInstance gameInstance) { plugin.debug("Difficulty:" + settings.getDifficulty()); plugin.debug("Time:" + settings.getTime()); - Optional hook = getHook(player.getUniqueId()); - if (hook.isPresent()) { - this.gamingPlayerMap.put(player.getUniqueId(), gameInstance.start(player, hook.get(), settings)); + FishHook hook = getHook(player.getUniqueId()); + if (hook != null) { + this.gamingPlayerMap.put(player.getUniqueId(), gameInstance.start(player, hook, settings)); } else { LogUtils.warn("It seems that player " + player.getName() + " is not fishing. Fishing game failed to start."); } } + /** + * Checks if a player with the given UUID has cast their fishing hook. + * + * @param uuid The UUID of the player to check. + * @return {@code true} if the player has cast their fishing hook, {@code false} otherwise. + */ @Override public boolean hasPlayerCastHook(UUID uuid) { FishHook fishHook = hookCacheMap.get(uuid); @@ -701,13 +726,42 @@ public class FishingManagerImpl implements Listener, FishingManager { return true; } + /** + * Sets the temporary fishing state for a player. + * + * @param player The player for whom to set the temporary fishing state. + * @param tempFishingState The temporary fishing state to set for the player. + */ @Override public void setTempFishingState(Player player, TempFishingState tempFishingState) { tempFishingStateMap.put(player.getUniqueId(), tempFishingState); } - @Override public void removeHookCheckTask(Player player) { hookCheckMap.remove(player.getUniqueId()); } + + /** + * Gets the {@link GamingPlayer} object associated with the given UUID. + * + * @param uuid The UUID of the player. + * @return The {@link GamingPlayer} object if found, or {@code null} if not found. + */ + @Override + @Nullable + public GamingPlayer getGamingPlayer(UUID uuid) { + return gamingPlayerMap.get(uuid); + } + + /** + * Gets the {@link TempFishingState} object associated with the given UUID. + * + * @param uuid The UUID of the player. + * @return The {@link TempFishingState} object if found, or {@code null} if not found. + */ + @Override + @Nullable + public TempFishingState getTempFishingState(UUID uuid) { + return tempFishingStateMap.get(uuid); + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java index 0aeb666f..3a8747f8 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java @@ -155,7 +155,7 @@ public class HookCheckTimerTask implements Runnable { } private void setTempState() { - Loot nextLoot = manager.getNextLoot(initialEffect, fishingPreparation); + Loot nextLoot = CustomFishingPlugin.get().getLootManager().getNextLoot(initialEffect, fishingPreparation); if (nextLoot == null) return; this.loot = nextLoot; diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java index af9c4250..d2501854 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java @@ -26,6 +26,7 @@ import net.momirealms.customfishing.api.mechanic.game.*; import net.momirealms.customfishing.api.util.FontUtils; import net.momirealms.customfishing.api.util.LogUtils; import net.momirealms.customfishing.api.util.OffsetUtils; +import net.momirealms.customfishing.mechanic.requirement.RequirementManagerImpl; import net.momirealms.customfishing.util.ClassUtils; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; @@ -42,13 +43,13 @@ public class GameManagerImpl implements GameManager { private final CustomFishingPlugin plugin; private final HashMap gameCreatorMap; - private final HashMap> gameMap; + private final HashMap> gameInstanceMap; private final String EXPANSION_FOLDER = "expansions/minigame"; public GameManagerImpl(CustomFishingPlugin plugin) { this.plugin = plugin; this.gameCreatorMap = new HashMap<>(); - this.gameMap = new HashMap<>(); + this.gameInstanceMap = new HashMap<>(); this.registerInbuiltGames(); } @@ -64,7 +65,7 @@ public class GameManagerImpl implements GameManager { } public void unload() { - this.gameMap.clear(); + this.gameInstanceMap.clear(); } public void disable() { @@ -72,6 +73,13 @@ public class GameManagerImpl implements GameManager { this.gameCreatorMap.clear(); } + /** + * Registers a new game type with the specified type identifier. + * + * @param type The type identifier for the game. + * @param gameFactory The {@link GameFactory} that creates instances of the game. + * @return {@code true} if the registration was successful, {@code false} if the type identifier is already registered. + */ @Override public boolean registerGameType(String type, GameFactory gameFactory) { if (gameCreatorMap.containsKey(type)) @@ -81,27 +89,56 @@ public class GameManagerImpl implements GameManager { return true; } + /** + * Unregisters a game type with the specified type identifier. + * + * @param type The type identifier of the game to unregister. + * @return {@code true} if the game type was successfully unregistered, {@code false} if the type identifier was not found. + */ @Override public boolean unregisterGameType(String type) { return gameCreatorMap.remove(type) != null; } + /** + * Retrieves the game factory associated with the specified game type. + * + * @param type The type identifier of the game. + * @return The {@code GameFactory} for the specified game type, or {@code null} if not found. + */ @Override @Nullable public GameFactory getGameFactory(String type) { return gameCreatorMap.get(type); } + /** + * Retrieves a game instance and its basic configuration associated with the specified key. + * + * @param key The key identifying the game instance. + * @return An {@code Optional} containing a {@code Pair} of the basic game configuration and the game instance + * if found, or an empty {@code Optional} if not found. + */ @Override - public Optional> getGame(String key) { - return Optional.ofNullable(gameMap.get(key)); + public Pair getGameInstance(String key) { + return gameInstanceMap.get(key); } + /** + * Retrieves a map of game names and their associated weights based on the specified conditions. + * + * @param condition The condition to evaluate game weights. + * @return A {@code HashMap} containing game names as keys and their associated weights as values. + */ @Override public HashMap getGameWithWeight(Condition condition) { - return plugin.getRequirementManager().getGameWithWeight(condition); + return ((RequirementManagerImpl) plugin.getRequirementManager()).getGameWithWeight(condition); } + /** + * Loads minigames from the plugin folder. + * This method searches for minigame configuration files in the plugin's data folder and loads them. + */ public void loadGamesFromPluginFolder() { Deque fileDeque = new ArrayDeque<>(); File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + "minigame"); @@ -124,11 +161,22 @@ public class GameManagerImpl implements GameManager { } } + /** + * Loads a minigame configuration from a YAML file. + * This method parses the YAML file and extracts minigame configurations to be used in the plugin. + * + * @param file The YAML file to load. + */ private void loadSingleFile(File file) { YamlConfiguration config = YamlConfiguration.loadConfiguration(file); for (Map.Entry entry : config.getValues(false).entrySet()) { if (entry.getValue() instanceof ConfigurationSection section) { GameFactory creator = this.getGameFactory(section.getString("game-type")); + if (creator == null) { + LogUtils.warn("Game type:" + section.getString("game-type") + " doesn't exist."); + continue; + } + BasicGameConfig.Builder basicGameBuilder = new BasicGameConfig.Builder(); Object time = section.get("time", 15); if (time instanceof String str) { @@ -144,9 +192,7 @@ public class GameManagerImpl implements GameManager { } else if (difficulty instanceof Integer integer) { basicGameBuilder.difficulty(integer); } - if (creator != null) { - gameMap.put(entry.getKey(), Pair.of(basicGameBuilder.build(), creator.setArgs(section))); - } + gameInstanceMap.put(entry.getKey(), Pair.of(basicGameBuilder.build(), creator.setArgs(section))); } } } @@ -451,6 +497,9 @@ public class GameManagerImpl implements GameManager { })); } + /** + * Loads minigame expansions from the expansion folder. + */ @SuppressWarnings("ResultOfMethodCallIgnored") private void loadExpansions() { File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/hook/HookManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/hook/HookManagerImpl.java index 7feacafa..23439f09 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/hook/HookManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/hook/HookManagerImpl.java @@ -67,6 +67,9 @@ public class HookManagerImpl implements Listener, HookManager { unload(); } + /** + * Loads configuration files for the specified types. + */ @SuppressWarnings("DuplicatedCode") private void loadConfig() { Deque fileDeque = new ArrayDeque<>(); @@ -92,6 +95,11 @@ public class HookManagerImpl implements Listener, HookManager { } } + /** + * Loads data from a single configuration file. + * + * @param file The configuration file to load. + */ private void loadSingleFile(File file) { YamlConfiguration config = YamlConfiguration.loadConfiguration(file); for (Map.Entry entry : config.getValues(false).entrySet()) { @@ -105,13 +113,120 @@ public class HookManagerImpl implements Listener, HookManager { } } + /** + * Get the hook setting by its ID. + * + * @param id The ID of the hook setting to retrieve. + * @return The hook setting with the given ID, or null if not found. + */ @Nullable @Override public HookSetting getHookSetting(String id) { return hookSettingMap.get(id); } + /** + * Decreases the durability of a fishing hook by a specified amount and optionally updates its lore. + * + * @param rod The fishing rod ItemStack to modify. + * @param amount The amount by which to decrease the durability. + * @param updateLore Whether to update the lore of the fishing rod. + */ + @Override + public void decreaseHookDurability(ItemStack rod, int amount, boolean updateLore) { + ItemUtils.decreaseHookDurability(rod, amount, updateLore); + } + + /** + * Increases the durability of a fishing hook by a specified amount and optionally updates its lore. + * + * @param rod The fishing rod ItemStack to modify. + * @param amount The amount by which to increase the durability. + * @param updateLore Whether to update the lore of the fishing rod. + */ + @Override + public void increaseHookDurability(ItemStack rod, int amount, boolean updateLore) { + ItemUtils.increaseHookDurability(rod, amount, updateLore); + } + + /** + * Sets the durability of a fishing hook to a specific amount and optionally updates its lore. + * + * @param rod The fishing rod ItemStack to modify. + * @param amount The new durability value to set. + * @param updateLore Whether to update the lore of the fishing rod. + */ + @Override + public void setHookDurability(ItemStack rod, int amount, boolean updateLore) { + ItemUtils.setHookDurability(rod, amount, updateLore); + } + + /** + * Equips a fishing hook on a fishing rod. + * + * @param rod The fishing rod ItemStack. + * @param hook The fishing hook ItemStack. + * @return True if the hook was successfully equipped, false otherwise. + */ + @Override + public boolean equipHookOnRod(ItemStack rod, ItemStack hook) { + if (rod == null || hook == null || hook.getType() == Material.AIR || hook.getAmount() != 1) + return false; + if (rod.getType() != Material.FISHING_ROD) + return false; + + String hookID = plugin.getItemManager().getAnyPluginItemID(hook); + HookSetting setting = getHookSetting(hookID); + if (setting == null) + return false; + + NBTItem rodNBTItem = new NBTItem(rod); + NBTCompound cfCompound = rodNBTItem.getOrCreateCompound("CustomFishing"); + + cfCompound.setString("hook_id", hookID); + cfCompound.setItemStack("hook_item", hook); + cfCompound.setInteger("hook_dur", ItemUtils.getDurability(hook)); + + ItemUtils.updateNBTItemLore(rodNBTItem); + rod.setItemMeta(rodNBTItem.getItem().getItemMeta()); + return true; + } + + /** + * Removes the fishing hook from a fishing rod. + * + * @param rod The fishing rod ItemStack. + * @return The removed fishing hook ItemStack, or null if no hook was found. + */ + @Override + public ItemStack removeHookFromRod(ItemStack rod) { + if (rod == null || rod.getType() != Material.FISHING_ROD) + return null; + + NBTItem rodNBTItem = new NBTItem(rod); + NBTCompound cfCompound = rodNBTItem.getCompound("CustomFishing"); + if (cfCompound == null) + return null; + + ItemStack hook = cfCompound.getItemStack("hook_item"); + if (hook != null) { + cfCompound.removeKey("hook_item"); + cfCompound.removeKey("hook_id"); + cfCompound.removeKey("hook_dur"); + ItemUtils.updateNBTItemLore(rodNBTItem); + rod.setItemMeta(rodNBTItem.getItem().getItemMeta()); + } + + return hook; + } + + /** + * Handles the event when a player clicks on a fishing rod in their inventory. + * + * @param event The InventoryClickEvent to handle. + */ @EventHandler + @SuppressWarnings("deprecation") public void onDragDrop(InventoryClickEvent event) { if (event.isCancelled()) return; @@ -123,14 +238,12 @@ public class HookManagerImpl implements Listener, HookManager { return; if (player.getGameMode() != GameMode.SURVIVAL) return; + if (plugin.getFishingManager().hasPlayerCastHook(player.getUniqueId())) + return; ItemStack cursor = event.getCursor(); if (cursor == null || cursor.getType() == Material.AIR) { if (event.getClick() == ClickType.RIGHT) { - if (plugin.getFishingManager().hasPlayerCastHook(player.getUniqueId())) { - return; - } - NBTItem nbtItem = new NBTItem(clicked); NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); if (cfCompound == null) @@ -150,16 +263,16 @@ public class HookManagerImpl implements Listener, HookManager { return; } - String hookID = plugin.getItemManager().getAnyItemID(cursor); + String hookID = plugin.getItemManager().getAnyPluginItemID(cursor); HookSetting setting = getHookSetting(hookID); if (setting == null) return; Condition condition = new Condition(player, new HashMap<>()); - condition.insertArg("{rod}", plugin.getItemManager().getAnyItemID(clicked)); - EffectCarrier effectCarrier = plugin.getEffectManager().getEffect("hook", hookID); + condition.insertArg("{rod}", plugin.getItemManager().getAnyPluginItemID(clicked)); + EffectCarrier effectCarrier = plugin.getEffectManager().getEffectCarrier("hook", hookID); if (effectCarrier != null) { - if (!RequirementManager.isRequirementsMet(effectCarrier.getRequirements(), condition)) { + if (!RequirementManager.isRequirementMet(condition, effectCarrier.getRequirements())) { return; } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java index e852091f..6a24ad92 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java @@ -101,6 +101,11 @@ public class ItemManagerImpl implements ItemManager, Listener { } } + /** + * Get a set of all item keys in the CustomFishing plugin. + * + * @return A set of item keys. + */ @Override public Set getAllItemsKey() { return buildableItemMap.keySet(); @@ -151,24 +156,29 @@ public class ItemManagerImpl implements ItemManager, Listener { } } - @Override - public boolean registerCustomItem(String namespace, String value, BuildableItem buildableItem) { - Key key = Key.of(namespace, value); - if (buildableItemMap.containsKey(key)) return false; - buildableItemMap.put(key, buildableItem); - return true; - } - - @Override - public boolean unregisterCustomItem(String namespace, String value) { - return buildableItemMap.remove(Key.of(namespace, value)) != null; - } - + /** + * Build an ItemStack with a specified namespace and value for a player. + * + * @param player The player for whom the ItemStack is being built. + * @param namespace The namespace of the item. + * @param value The value of the item. + * @return The constructed ItemStack. + */ @Override public ItemStack build(Player player, String namespace, String value) { return build(player, namespace, value, new HashMap<>()); } + /** + * Build an ItemStack with a specified namespace and value, replacing placeholders, + * for a player. + * + * @param player The player for whom the ItemStack is being built. + * @param namespace The namespace of the item. + * @param value The value of the item. + * @param placeholders The placeholders to replace in the item's attributes. + * @return The constructed ItemStack, or null if the item doesn't exist. + */ @Override public ItemStack build(Player player, String namespace, String value, Map placeholders) { BuildableItem buildableItem = buildableItemMap.get(Key.of(namespace, value)); @@ -176,20 +186,42 @@ public class ItemManagerImpl implements ItemManager, Listener { return buildableItem.build(player, placeholders); } + /** + * Build an ItemStack using an ItemBuilder for a player. + * + * @param player The player for whom the ItemStack is being built. + * @param builder The ItemBuilder used to construct the ItemStack. + * @return The constructed ItemStack. + */ @NotNull @Override public ItemStack build(Player player, ItemBuilder builder) { return build(player, builder, new HashMap<>()); } + /** + * Retrieve a BuildableItem by its namespace and value. + * + * @param namespace The namespace of the BuildableItem. + * @param value The value of the BuildableItem. + * @return The BuildableItem with the specified namespace and value, or null if not found. + */ @Override @Nullable public BuildableItem getBuildableItem(String namespace, String value) { return buildableItemMap.get(Key.of(namespace, value)); } + /** + * Get the item ID associated with the given ItemStack by checking all available item libraries. + * The detection order is determined by the configuration. + * + * @param itemStack The ItemStack to retrieve the item ID from. + * @return The item ID or "AIR" if not found or if the ItemStack is null or empty. + */ + @NotNull @Override - public String getAnyItemID(ItemStack itemStack) { + public String getAnyPluginItemID(ItemStack itemStack) { for (String plugin : CFConfig.itemDetectOrder) { ItemLibrary itemLibrary = itemLibraryMap.get(plugin); if (itemLibrary != null) { @@ -200,11 +232,18 @@ public class ItemManagerImpl implements ItemManager, Listener { } } // should not reach this because vanilla library would always work - return null; + return "AIR"; } + /** + * Build an ItemStack for a player based on the provided item ID. + * + * @param player The player for whom the ItemStack is being built. + * @param id The item ID, which may include a namespace (e.g., "namespace:id"). + * @return The constructed ItemStack or null if the ID is not valid. + */ @Override - public ItemStack buildAnyItemByID(Player player, String id) { + public ItemStack buildAnyPluginItemByID(Player player, String id) { if (id.contains(":")) { String[] split = id.split(":", 2); return itemLibraryMap.get(split[0]).buildItem(player, split[1]); @@ -213,16 +252,28 @@ public class ItemManagerImpl implements ItemManager, Listener { } } + /** + * Checks if the provided ItemStack is a custom fishing item + * + * @param itemStack The ItemStack to check. + * @return True if the ItemStack is a custom fishing item; otherwise, false. + */ @Override public boolean isCustomFishingItem(ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) return false; NBTItem nbtItem = new NBTItem(itemStack); - return nbtItem.hasTag("CustomFishing"); + return nbtItem.hasTag("CustomFishing") && !nbtItem.getCompound("CustomFishing").getString("id").equals(""); } + /** + * Get the item ID associated with the given ItemStack, if available. + * + * @param itemStack The ItemStack to retrieve the item ID from. + * @return The item ID or null if not found or if the ItemStack is null or empty. + */ @Nullable @Override - public String getItemID(ItemStack itemStack) { + public String getCustomFishingItemID(ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) return null; NBTItem nbtItem = new NBTItem(itemStack); NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); @@ -230,6 +281,14 @@ public class ItemManagerImpl implements ItemManager, Listener { return cfCompound.getString("id"); } + /** + * Create a CFBuilder instance for an item configuration section + * + * @param section The configuration section containing item settings. + * @param type The type of the item (e.g., "rod", "bait"). + * @param id The unique identifier for the item. + * @return A CFBuilder instance representing the configured item, or null if the section is null. + */ @Nullable @Override public CFBuilder getItemBuilder(ConfigurationSection section, String type, String id) { @@ -265,7 +324,16 @@ public class ItemManagerImpl implements ItemManager, Listener { return itemCFBuilder; } + /** + * Build an ItemStack using the provided ItemBuilder, player, and placeholders. + * + * @param player The player for whom the item is being built. + * @param builder The ItemBuilder that defines the item's properties. + * @param placeholders A map of placeholders and their corresponding values to be applied to the item. + * @return The constructed ItemStack. + */ @Override + @NotNull public ItemStack build(Player player, ItemBuilder builder, Map placeholders) { ItemStack temp = itemLibraryMap.get(builder.getLibrary()).buildItem(player, builder.getId()); temp.setAmount(builder.getAmount()); @@ -277,6 +345,12 @@ public class ItemManagerImpl implements ItemManager, Listener { return nbtItem.getItem(); } + /** + * Register an item library. + * + * @param itemLibrary The item library to register. + * @return True if the item library was successfully registered, false if it already exists. + */ @Override public boolean registerItemLibrary(ItemLibrary itemLibrary) { if (itemLibraryMap.containsKey(itemLibrary.identification())) return false; @@ -284,21 +358,31 @@ public class ItemManagerImpl implements ItemManager, Listener { return true; } + /** + * Unregister an item library. + * + * @param identification The item library to unregister. + * @return True if the item library was successfully unregistered, false if it doesn't exist. + */ @Override - public boolean unRegisterItemLibrary(ItemLibrary itemLibrary) { - return itemLibraryMap.remove(itemLibrary.identification(), itemLibrary); + public boolean unRegisterItemLibrary(String identification) { + return itemLibraryMap.remove(identification) != null; } + /** + * Drops an item based on the provided loot, applying velocity from a hook location to a player location. + * + * @param player The player for whom the item is intended. + * @param hookLocation The location where the item will initially drop. + * @param playerLocation The target location towards which the item's velocity is applied. + * @param id The loot object representing the item to be dropped. + * @param args A map of placeholders for item customization. + */ @Override - public boolean unRegisterItemLibrary(String itemLibrary) { - return itemLibraryMap.remove(itemLibrary) != null; - } - - @Override - public void dropItem(Player player, Location hookLocation, Location playerLocation, Loot loot, Map args) { - ItemStack item = build(player, "item", loot.getID(), args); + public void dropItem(Player player, Location hookLocation, Location playerLocation, String id, Map args) { + ItemStack item = build(player, "item", id, args); if (item == null) { - LogUtils.warn(String.format("Item %s not exists", loot.getID())); + LogUtils.warn(String.format("Item %s not exists", id)); return; } if (item.getType() == Material.AIR) { @@ -310,6 +394,13 @@ public class ItemManagerImpl implements ItemManager, Listener { itemEntity.setVelocity(vector); } + /** + * Drops an item entity at the specified location and applies velocity towards another location. + * + * @param hookLocation The location where the item will initially drop. + * @param playerLocation The target location towards which the item's velocity is applied. + * @param itemStack The item stack to be dropped as an entity. + */ @Override public void dropItem(Location hookLocation, Location playerLocation, ItemStack itemStack) { Entity itemEntity = hookLocation.getWorld().dropItem(hookLocation, itemStack); @@ -318,6 +409,42 @@ public class ItemManagerImpl implements ItemManager, Listener { itemEntity.setVelocity(vector); } + /** + * Decreases the durability of an ItemStack by a specified amount and optionally updates its lore. + * + * @param itemStack The ItemStack to modify. + * @param amount The amount by which to decrease the durability. + * @param updateLore Whether to update the lore of the ItemStack. + */ + @Override + public void decreaseDurability(ItemStack itemStack, int amount, boolean updateLore) { + ItemUtils.decreaseDurability(itemStack, amount, updateLore); + } + + /** + * Increases the durability of an ItemStack by a specified amount and optionally updates its lore. + * + * @param itemStack The ItemStack to modify. + * @param amount The amount by which to increase the durability. + * @param updateLore Whether to update the lore of the ItemStack. + */ + @Override + public void increaseDurability(ItemStack itemStack, int amount, boolean updateLore) { + ItemUtils.increaseDurability(itemStack, amount, updateLore); + } + + /** + * Sets the durability of an ItemStack to a specific amount and optionally updates its lore. + * + * @param itemStack The ItemStack to modify. + * @param amount The new durability value. + * @param updateLore Whether to update the lore of the ItemStack. + */ + @Override + public void setDurability(ItemStack itemStack, int amount, boolean updateLore) { + ItemUtils.setDurability(itemStack, amount, updateLore); + } + @NotNull private List> getEnchantmentPair(ConfigurationSection section) { List> list = new ArrayList<>(); @@ -526,7 +653,7 @@ public class ItemManagerImpl implements ItemManager, Listener { placeholders.put("{bonus}", String.format("%.2f", bonus)); } float size = Float.parseFloat(placeholders.getOrDefault("{size}", "0")); - double price = CustomFishingPlugin.get().getMarketManager().getPrice( + double price = CustomFishingPlugin.get().getMarketManager().getFishPrice( base, bonus, size @@ -665,7 +792,7 @@ public class ItemManagerImpl implements ItemManager, Listener { public void onConsumeItem(PlayerItemConsumeEvent event) { if (event.isCancelled()) return; ItemStack itemStack = event.getItem(); - String id = getAnyItemID(itemStack); + String id = getAnyPluginItemID(itemStack); Loot loot = plugin.getLootManager().getLoot(id); if (loot != null) { Condition condition = new Condition(event.getPlayer()); @@ -697,7 +824,7 @@ public class ItemManagerImpl implements ItemManager, Listener { NBTCompound compound = nbtItem.getCompound("CustomFishing"); if (compound == null) return; event.setCancelled(true); - ItemUtils.addDurability(itemStack, event.getRepairAmount(), true); + ItemUtils.increaseDurability(itemStack, event.getRepairAmount(), true); } @EventHandler @@ -710,12 +837,12 @@ public class ItemManagerImpl implements ItemManager, Listener { if (event.getAction() != org.bukkit.event.block.Action.RIGHT_CLICK_AIR || event.getAction() != org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK) return; - String id = getAnyItemID(itemStack); - EffectCarrier carrier = plugin.getEffectManager().getEffect("util", id); + String id = getAnyPluginItemID(itemStack); + EffectCarrier carrier = plugin.getEffectManager().getEffectCarrier("util", id); if (carrier == null) return; Condition condition = new Condition(event.getPlayer()); - if (!RequirementManager.isRequirementsMet(carrier.getRequirements(), condition)) + if (!RequirementManager.isRequirementMet(condition, carrier.getRequirements())) return; Action[] actions = carrier.getActions(ActionTrigger.INTERACT); if (actions != null) diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/loot/LootManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/loot/LootManagerImpl.java index 952c6b0d..a4fb9e29 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/loot/LootManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/loot/LootManagerImpl.java @@ -18,16 +18,22 @@ package net.momirealms.customfishing.mechanic.loot; import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.common.Pair; import net.momirealms.customfishing.api.manager.LootManager; -import net.momirealms.customfishing.api.mechanic.action.Action; import net.momirealms.customfishing.api.mechanic.condition.Condition; +import net.momirealms.customfishing.api.mechanic.effect.Effect; import net.momirealms.customfishing.api.mechanic.loot.CFLoot; import net.momirealms.customfishing.api.mechanic.loot.Loot; import net.momirealms.customfishing.api.mechanic.loot.LootType; +import net.momirealms.customfishing.api.mechanic.loot.WeightModifier; import net.momirealms.customfishing.api.util.LogUtils; +import net.momirealms.customfishing.api.util.WeightUtils; +import net.momirealms.customfishing.mechanic.requirement.RequirementManagerImpl; import net.momirealms.customfishing.util.ConfigUtils; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; @@ -36,7 +42,9 @@ import java.util.*; public class LootManagerImpl implements LootManager { private final CustomFishingPlugin plugin; + // A map that associates loot IDs with their respective loot configurations. private final HashMap lootMap; + // A map that associates loot group IDs with lists of loot IDs. private final HashMap> lootGroupMap; public LootManagerImpl(CustomFishingPlugin plugin) { @@ -58,6 +66,12 @@ public class LootManagerImpl implements LootManager { unload(); } + /** + * Loads loot configurations from the plugin's content folders. + * This method scans the "item," "entity," and "block" subfolders within the plugin's data folder + * and loads loot configurations from YAML files. + * If the subfolders or default loot files don't exist, it creates them. + */ @SuppressWarnings("DuplicatedCode") public void loadLootsFromPluginFolder() { Deque fileDeque = new ArrayDeque<>(); @@ -83,33 +97,122 @@ public class LootManagerImpl implements LootManager { } } + /** + * Retrieves a list of loot IDs associated with a loot group key. + * + * @param key The key of the loot group. + * @return A list of loot IDs belonging to the specified loot group, or null if not found. + */ @Nullable @Override public List getLootGroup(String key) { return lootGroupMap.get(key); } + /** + * Retrieves a loot configuration based on a provided loot key. + * + * @param key The key of the loot configuration. + * @return The Loot object associated with the specified loot key, or null if not found. + */ @Nullable @Override public Loot getLoot(String key) { return lootMap.get(key); } + /** + * Retrieves a collection of all loot configuration keys. + * + * @return A collection of all loot configuration keys. + */ @Override public Collection getAllLootKeys() { return lootMap.keySet(); } + /** + * Retrieves a collection of all loot configurations. + * + * @return A collection of all loot configurations. + */ @Override public Collection getAllLoots() { return lootMap.values(); } + /** + * Retrieves loot configurations with weights based on a given condition. + * + * @param condition The condition used to filter loot configurations. + * @return A mapping of loot configuration keys to their associated weights. + */ @Override public HashMap getLootWithWeight(Condition condition) { - return plugin.getRequirementManager().getLootWithWeight(condition); + return ((RequirementManagerImpl) plugin.getRequirementManager()).getLootWithWeight(condition); } + /** + * Get a collection of possible loot keys based on a given condition. + * + * @param condition The condition to determine possible loot. + * @return A collection of loot keys. + */ + @Override + public Collection getPossibleLootKeys(Condition condition) { + return ((RequirementManagerImpl) plugin.getRequirementManager()).getLootWithWeight(condition).keySet(); + } + + /** + * Get a map of possible loot keys with their corresponding weights, considering fishing effect and condition. + * + * @param initialEffect The effect to apply weight modifiers. + * @param condition The condition to determine possible loot. + * @return A map of loot keys and their weights. + */ + @NotNull + @Override + public Map getPossibleLootKeysWithWeight(Effect initialEffect, Condition condition) { + Map lootWithWeight = ((RequirementManagerImpl) plugin.getRequirementManager()).getLootWithWeight(condition); + + Player player = condition.getPlayer(); + for (Pair pair : initialEffect.getWeightModifier()) { + Double previous = lootWithWeight.get(pair.left()); + if (previous != null) + lootWithWeight.put(pair.left(), pair.right().modify(player, previous)); + } + for (Pair pair : initialEffect.getWeightModifierIgnored()) { + double previous = lootWithWeight.getOrDefault(pair.left(), 0d); + lootWithWeight.put(pair.left(), pair.right().modify(player, previous)); + } + return lootWithWeight; + } + + /** + * Get the next loot item based on fishing effect and condition. + * + * @param initialEffect The effect to apply weight modifiers. + * @param condition The condition to determine possible loot. + * @return The next loot item, or null if it doesn't exist. + */ + @Override + @Nullable + public Loot getNextLoot(Effect initialEffect, Condition condition) { + String key = WeightUtils.getRandom(getPossibleLootKeysWithWeight(initialEffect, condition)); + Loot loot = getLoot(key); + if (loot == null) { + LogUtils.warn(String.format("Loot %s doesn't exist.", key)); + return null; + } + return loot; + } + + /** + * Loads loot configurations from a single YAML file and populates the lootMap and lootGroupMap. + * + * @param file The YAML file containing loot configurations. + * @param namespace The namespace indicating the type of loot (e.g., "item," "entity," "block"). + */ private void loadSingleFile(File file, String namespace) { YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file); for (Map.Entry entry : yaml.getValues(false).entrySet()) { @@ -119,12 +222,14 @@ public class LootManagerImpl implements LootManager { namespace, entry.getKey() ); + // Check for duplicate loot configurations and log an error if found. if (lootMap.containsKey(entry.getKey())) { LogUtils.severe("Duplicated loot found: " + entry.getKey() + "."); } else { lootMap.put(entry.getKey(), loot); } String[] group = loot.getLootGroup(); + // If the loot configuration belongs to one or more groups, update lootGroupMap. if (group != null) { for (String g : group) { List groupMembers = lootGroupMap.computeIfAbsent(g, k -> new ArrayList<>()); @@ -135,6 +240,14 @@ public class LootManagerImpl implements LootManager { } } + /** + * Creates a single loot configuration item from a ConfigurationSection. + * + * @param section The ConfigurationSection containing loot configuration data. + * @param namespace The namespace indicating the type of loot (e.g., "item," "entity," "block"). + * @param key The unique key identifying the loot configuration. + * @return A CFLoot object representing the loot configuration. + */ private CFLoot getSingleSectionItem(ConfigurationSection section, String namespace, String key) { return new CFLoot.Builder(key, LootType.valueOf(namespace.toUpperCase(Locale.ENGLISH))) .disableStats(section.getBoolean("disable-stat", false)) @@ -146,18 +259,7 @@ public class LootManagerImpl implements LootManager { .lootGroup(ConfigUtils.stringListArgs(section.get("group")).toArray(new String[0])) .nick(section.getString("nick", section.getString("display.name", key))) .addActions(plugin.getActionManager().getActionMap(section.getConfigurationSection("events"))) - .addTimesActions(getTimesActionMap(section.getConfigurationSection("events.success-times"))) + .addTimesActions(plugin.getActionManager().getTimesActionMap(section.getConfigurationSection("events.success-times"))) .build(); } - - private HashMap getTimesActionMap(ConfigurationSection section) { - HashMap actionMap = new HashMap<>(); - if (section == null) return actionMap; - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - actionMap.put(Integer.parseInt(entry.getKey()), plugin.getActionManager().getActions(innerSection)); - } - } - return actionMap; - } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUI.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUI.java index cb93943d..61f590eb 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUI.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUI.java @@ -34,13 +34,22 @@ import java.util.Map; public class MarketGUI { + // A map that associates characters with MarketGUI elements. private final HashMap itemsCharMap; + // A map that associates slot indices with MarketGUI elements. private final HashMap itemsSlotMap; private final Inventory inventory; private final MarketManagerImpl manager; private final Player owner; private final EarningData earningData; + /** + * Constructor for creating a MarketGUI. + * + * @param manager The Market Manager implementation associated with this MarketGUI. + * @param player The player who owns this MarketGUI. + * @param earningData Data related to earnings for this MarketGUI. + */ public MarketGUI(MarketManagerImpl manager, Player player, EarningData earningData) { this.manager = manager; this.owner = player; @@ -56,6 +65,9 @@ public class MarketGUI { holder.setInventory(this.inventory); } + /** + * Initialize the GUI layout by mapping elements to inventory slots. + */ private void init() { int line = 0; for (String content : manager.getLayout()) { @@ -79,6 +91,12 @@ public class MarketGUI { } } + /** + * Add one or more elements to the GUI. + * @param elements Elements to be added. + * @return The MarketGUI instance. + */ + @SuppressWarnings("UnusedReturnValue") public MarketGUI addElement(MarketGUIElement... elements) { for (MarketGUIElement element : elements) { itemsCharMap.put(element.getSymbol(), element); @@ -86,26 +104,47 @@ public class MarketGUI { return this; } + /** + * Build and initialize the GUI. + */ public MarketGUI build() { init(); return this; } + /** + * Show the GUI to a player if the player is the owner. + * @param player The player to show the GUI to. + */ public void show(Player player) { if (player != owner) return; player.openInventory(inventory); } + /** + * Get the MarketGUIElement associated with a specific inventory slot. + * @param slot The slot index in the inventory. + * @return The associated MarketGUIElement or null if not found. + */ @Nullable public MarketGUIElement getElement(int slot) { return itemsSlotMap.get(slot); } + /** + * Get the MarketGUIElement associated with a specific character symbol. + * @param slot The character symbol. + * @return The associated MarketGUIElement or null if not found. + */ @Nullable public MarketGUIElement getElement(char slot) { return itemsCharMap.get(slot); } + /** + * Refresh the GUI, updating the display based on current data. + * @return The MarketGUI instance. + */ public MarketGUI refresh() { double totalWorth = getTotalWorth(); MarketDynamicGUIElement functionElement = (MarketDynamicGUIElement) getElement(manager.getFunctionSlot()); @@ -145,6 +184,10 @@ public class MarketGUI { return this; } + /** + * Calculate and return the total worth of items in the inventory. + * @return The total worth of items. + */ public double getTotalWorth() { double money = 0d; MarketGUIElement itemElement = getElement(manager.getItemSlot()); @@ -158,10 +201,17 @@ public class MarketGUI { return money; } + /** + * Get the inventory associated with this MarketGUI. + * @return The Inventory object. + */ public Inventory getInventory() { return inventory; } + /** + * Clear items with non-zero value from the inventory. + */ public void clearWorthyItems() { MarketGUIElement itemElement = getElement(manager.getItemSlot()); if (itemElement == null) { @@ -175,6 +225,10 @@ public class MarketGUI { } } + /** + * Get an empty slot in the item section of the inventory. + * @return The index of an empty slot or -1 if none are found. + */ public int getEmptyItemSlot() { MarketGUIElement itemElement = getElement(manager.getItemSlot()); if (itemElement == null) { @@ -189,6 +243,9 @@ public class MarketGUI { return -1; } + /** + * Return items to the owner's inventory. + */ public void returnItems() { MarketGUIElement itemElement = getElement(manager.getItemSlot()); if (itemElement == null) { @@ -203,6 +260,10 @@ public class MarketGUI { } } + /** + * Get the earning data associated with this MarketGUI. + * @return The EarningData object. + */ public EarningData getEarningData() { return earningData; } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUIElement.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUIElement.java index c5952ff0..774b12cb 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUIElement.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUIElement.java @@ -34,18 +34,22 @@ public class MarketGUIElement { this.slots = new ArrayList<>(); } + // Method to add a slot to the list of slots for this element public void addSlot(int slot) { slots.add(slot); } + // Getter method to retrieve the symbol associated with this element public char getSymbol() { return symbol; } + // Getter method to retrieve the cloned ItemStack associated with this element public ItemStack getItemStack() { - return itemStack; + return itemStack.clone(); } + // Getter method to retrieve the list of slots where this element can appear public List getSlots() { return slots; } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java index 2f10a6be..2d025027 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java @@ -93,10 +93,13 @@ public class MarketManagerImpl implements MarketManager, Listener { unload(); } + // Load configuration from the plugin's config file private void loadConfig() { YamlConfiguration config = plugin.getConfig("market.yml"); this.enable = config.getBoolean("enable", true); if (!this.enable) return; + + // Load various configuration settings this.layout = config.getStringList("layout").toArray(new String[0]); this.title = config.getString("title", "market.title"); this.formula = config.getString("price-formula", "{base} + {bonus} * {size}"); @@ -111,12 +114,15 @@ public class MarketManagerImpl implements MarketManager, Listener { this.earningLimit = config.getBoolean("limitation.enable", true) ? config.getDouble("limitation.earnings", 100) : -1; this.allowItemWithNoPrice = config.getBoolean("item-slot.allow-items-with-no-price", true); + // Load item prices from the configuration ConfigurationSection priceSection = config.getConfigurationSection("item-price"); if (priceSection != null) { for (Map.Entry entry : priceSection.getValues(false).entrySet()) { this.priceMap.put(entry.getKey(), ConfigUtils.getDoubleValue(entry.getValue())); } } + + // Load decorative icons from the configuration ConfigurationSection decorativeSection = config.getConfigurationSection("decorative-icons"); if (decorativeSection != null) { for (Map.Entry entry : decorativeSection.getValues(false).entrySet()) { @@ -129,8 +135,14 @@ public class MarketManagerImpl implements MarketManager, Listener { } } + /** + * Open the market GUI for a player + * + * @param player player + */ @Override public void openMarketGUI(Player player) { + if (!isEnable()) return; OnlineUser user = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); if (user == null) { LogUtils.warn("Player " + player.getName() + "'s market data is not loaded yet."); @@ -147,6 +159,11 @@ public class MarketManagerImpl implements MarketManager, Listener { marketGUIMap.put(player.getUniqueId(), gui); } + /** + * This method handles the closing of an inventory. + * + * @param event The InventoryCloseEvent that triggered this method. + */ @EventHandler public void onCloseInv(InventoryCloseEvent event) { if (!(event.getPlayer() instanceof Player player)) @@ -158,6 +175,11 @@ public class MarketManagerImpl implements MarketManager, Listener { gui.returnItems(); } + /** + * This method handles a player quitting the server. + * + * @param event The PlayerQuitEvent that triggered this method. + */ @EventHandler public void onQuit(PlayerQuitEvent event) { MarketGUI gui = marketGUIMap.remove(event.getPlayer().getUniqueId()); @@ -165,6 +187,11 @@ public class MarketManagerImpl implements MarketManager, Listener { gui.returnItems(); } + /** + * This method handles dragging items in an inventory. + * + * @param event The InventoryDragEvent that triggered this method. + */ @EventHandler public void onDragInv(InventoryDragEvent event) { if (event.isCancelled()) @@ -197,14 +224,23 @@ public class MarketManagerImpl implements MarketManager, Listener { plugin.getScheduler().runTaskSyncLater(gui::refresh, player.getLocation(), 50, TimeUnit.MILLISECONDS); } + /** + * This method handles inventory click events. + * + * @param event The InventoryClickEvent that triggered this method. + */ @EventHandler public void onClickInv(InventoryClickEvent event) { if (event.isCancelled()) return; + Inventory clickedInv = event.getClickedInventory(); if (clickedInv == null) return; + Player player = (Player) event.getWhoClicked(); + + // Check if the clicked inventory is a MarketGUI if (!(event.getInventory().getHolder() instanceof MarketGUIHolder)) return; @@ -241,14 +277,14 @@ public class MarketManagerImpl implements MarketManager, Listener { ))); if (worth > 0) { if (earningLimit != -1 && (earningLimit - data.earnings) < worth) { - // can't earn more money + // Can't earn more money if (limitActions != null) { for (Action action : limitActions) { action.trigger(condition); } } } else { - // clear items + // Clear items and update earnings gui.clearWorthyItems(); data.earnings += worth; condition.insertArg("{rest}", String.format("%.2f", (earningLimit - data.earnings))); @@ -259,7 +295,7 @@ public class MarketManagerImpl implements MarketManager, Listener { } } } else { - // nothing to sell + // Nothing to sell if (denyActions != null) { for (Action action : denyActions) { action.trigger(condition); @@ -268,6 +304,7 @@ public class MarketManagerImpl implements MarketManager, Listener { } } } else { + // Handle interactions with the player's inventory ItemStack current = event.getCurrentItem(); if (!allowItemWithNoPrice) { double price = getItemPrice(current); @@ -308,38 +345,69 @@ public class MarketManagerImpl implements MarketManager, Listener { } } + // Refresh the GUI plugin.getScheduler().runTaskSyncLater(gui::refresh, player.getLocation(), 50, TimeUnit.MILLISECONDS); } + /** + * Retrieves the current date as an integer in the format MMDD (e.g., September 21 as 0921). + * + * @return An integer representing the current date. + */ @Override public int getDate() { Calendar calendar = Calendar.getInstance(); return (calendar.get(Calendar.MONTH) +1) * 100 + calendar.get(Calendar.DATE); } + /** + * Calculates the price of an ItemStack based on custom data or a predefined price map. + * + * @param itemStack The ItemStack for which the price is calculated. + * @return The calculated price of the ItemStack. + */ @Override public double getItemPrice(ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) return 0; + NBTItem nbtItem = new NBTItem(itemStack); Double price = nbtItem.getDouble("Price"); if (price != null && price != 0) { + // If a custom price is defined in the ItemStack's NBT data, use it. return price * itemStack.getAmount(); } + + // If no custom price is defined, attempt to fetch the price from a predefined price map. String itemID = itemStack.getType().name(); if (nbtItem.hasTag("CustomModelData")) { itemID = itemID + ":" + nbtItem.getInteger("CustomModelData"); } + + // Use the price from the price map, or default to 0 if not found. return priceMap.getOrDefault(itemID, 0d) * itemStack.getAmount(); } + /** + * Retrieves the formula used for calculating prices. + * + * @return The pricing formula as a string. + */ @Override public String getFormula() { return formula; } + /** + * Calculates the price based on a formula with provided variables. + * + * @param base The base value for the formula. + * @param bonus The bonus value for the formula. + * @param size The size value for the formula. + * @return The calculated price based on the formula and provided variables. + */ @Override - public double getPrice(float base, float bonus, float size) { + public double getFishPrice(float base, float bonus, float size) { Expression expression = new ExpressionBuilder(getFormula()) .variables("base", "bonus", "size") .build() @@ -349,38 +417,88 @@ public class MarketManagerImpl implements MarketManager, Listener { return expression.evaluate(); } + /** + * Gets the character representing the item slot in the MarketGUI. + * + * @return The item slot character. + */ + @Override public char getItemSlot() { return itemSlot; } + /** + * Gets the character representing the function slot in the MarketGUI. + * + * @return The function slot character. + */ + @Override public char getFunctionSlot() { return functionSlot; } + /** + * Gets the layout of the MarketGUI as an array of strings. + * + * @return The layout of the MarketGUI. + */ + @Override public String[] getLayout() { return layout; } + /** + * Gets the title of the MarketGUI. + * + * @return The title of the MarketGUI. + */ + @Override public String getTitle() { return title; } + /** + * Gets the earning limit + * + * @return The earning limit + */ + @Override public double getEarningLimit() { return earningLimit; } + /** + * Gets the builder for the function icon representing the limit in the MarketGUI. + * + * @return The function icon builder for the limit. + */ public BuildableItem getFunctionIconLimitBuilder() { return functionIconLimitBuilder; } + /** + * Gets the builder for the function icon representing allow actions in the MarketGUI. + * + * @return The function icon builder for allow actions. + */ public BuildableItem getFunctionIconAllowBuilder() { return functionIconAllowBuilder; } + /** + * Gets the builder for the function icon representing deny actions in the MarketGUI. + * + * @return The function icon builder for deny actions. + */ public BuildableItem getFunctionIconDenyBuilder() { return functionIconDenyBuilder; } + /** + * Is market enabled + * + * @return enable or not + */ @Override public boolean isEnable() { return enable; diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/CoolDownManager.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/CoolDownManager.java index 74a9833e..acfffad8 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/CoolDownManager.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/CoolDownManager.java @@ -28,6 +28,10 @@ import java.util.HashMap; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +/** + * Manages cooldowns for various actions or events. + * Keeps track of cooldown times for different keys associated with player UUIDs. + */ public class CoolDownManager implements Listener { private final ConcurrentHashMap dataMap; @@ -38,6 +42,14 @@ public class CoolDownManager implements Listener { this.plugin = plugin; } + /** + * Checks if a player is currently in cooldown for a specific key. + * + * @param uuid The UUID of the player. + * @param key The key associated with the cooldown. + * @param time The cooldown time in milliseconds. + * @return True if the player is in cooldown, false otherwise. + */ public boolean isCoolDown(UUID uuid, String key, long time) { Data data = this.dataMap.computeIfAbsent(uuid, k -> new Data()); return data.isCoolDown(key, time); @@ -56,6 +68,11 @@ public class CoolDownManager implements Listener { this.dataMap.clear(); } + /** + * Event handler for when a player quits the game. Removes their cooldown data. + * + * @param event The PlayerQuitEvent triggered when a player quits. + */ @EventHandler public void onQuit(PlayerQuitEvent event) { dataMap.remove(event.getPlayer().getUniqueId()); @@ -69,14 +86,21 @@ public class CoolDownManager implements Listener { this.coolDownMap = new HashMap<>(); } + /** + * Checks if the player is in cooldown for a specific key. + * + * @param key The key associated with the cooldown. + * @param delay The cooldown delay in milliseconds. + * @return True if the player is in cooldown, false otherwise. + */ public synchronized boolean isCoolDown(String key, long delay) { long time = System.currentTimeMillis(); long last = coolDownMap.getOrDefault(key, time - delay); if (last + delay > time) { - return true; + return true; // Player is in cooldown } else { coolDownMap.put(key, time); - return false; + return false; // Player is not in cooldown } } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/ConditionalElement.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/ConditionalElement.java index 2869effe..c39e592c 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/ConditionalElement.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/ConditionalElement.java @@ -35,13 +35,19 @@ public class ConditionalElement { public ConditionalElement( Requirement[] requirements, List> modifierList, - HashMap subLoots + HashMap subElements ) { this.modifierList = modifierList; this.requirements = requirements; - this.subLoots = subLoots; + this.subLoots = subElements; } + /** + * Combines the weight modifiers for this element. + * + * @param player The player for whom the modifiers are applied. + * @param weightMap The map of weight modifiers. + */ synchronized public void combine(Player player, HashMap weightMap) { for (Pair modifierPair : this.modifierList) { double previous = weightMap.getOrDefault(modifierPair.left(), 0d); @@ -49,16 +55,11 @@ public class ConditionalElement { } } - public boolean isConditionsMet(Condition condition) { - for (Requirement requirement : requirements) { - if (!requirement.isConditionMet(condition)) { - return false; - } - } - return true; + public Requirement[] getRequirements() { + return requirements; } - public HashMap getSubLoots() { + public HashMap getSubElements() { return subLoots; } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/EmptyRequirement.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/EmptyRequirement.java index 8f926604..f01645be 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/EmptyRequirement.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/EmptyRequirement.java @@ -20,6 +20,9 @@ package net.momirealms.customfishing.mechanic.requirement; import net.momirealms.customfishing.api.mechanic.condition.Condition; import net.momirealms.customfishing.api.mechanic.requirement.Requirement; +/** + * Represents an empty requirement that always returns true when checking conditions. + */ public class EmptyRequirement implements Requirement { public static EmptyRequirement instance = new EmptyRequirement(); diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java index 0519d7f7..a8ae218f 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java @@ -82,10 +82,15 @@ public class RequirementManagerImpl implements RequirementManager { this.conditionalLootsMap.clear(); } + /** + * Loads requirement group configuration data from various configuration files. + */ public void loadRequirementGroupFileConfig() { + // Load mechanic requirements from the main configuration file YamlConfiguration main = plugin.getConfig("config.yml"); mechanicRequirements = getRequirements(main.getConfigurationSection("mechanics.mechanic-requirements"), true); + // Load conditional loot data from the loot conditions configuration file YamlConfiguration config1 = plugin.getConfig("loot-conditions.yml"); for (Map.Entry entry : config1.getValues(false).entrySet()) { if (entry.getValue() instanceof ConfigurationSection section) { @@ -93,6 +98,7 @@ public class RequirementManagerImpl implements RequirementManager { } } + // Load conditional game data from the game conditions configuration file YamlConfiguration config2 = plugin.getConfig("game-conditions.yml"); for (Map.Entry entry : config2.getValues(false).entrySet()) { if (entry.getValue() instanceof ConfigurationSection section) { @@ -101,6 +107,13 @@ public class RequirementManagerImpl implements RequirementManager { } } + /** + * Registers a custom requirement type with its corresponding factory. + * + * @param type The type identifier of the requirement. + * @param requirementFactory The factory responsible for creating instances of the requirement. + * @return True if registration was successful, false if the type is already registered. + */ @Override public boolean registerRequirement(String type, RequirementFactory requirementFactory) { if (this.requirementBuilderMap.containsKey(type)) return false; @@ -108,11 +121,46 @@ public class RequirementManagerImpl implements RequirementManager { return true; } + /** + * Unregisters a custom requirement type. + * + * @param type The type identifier of the requirement to unregister. + * @return True if unregistration was successful, false if the type is not registered. + */ @Override public boolean unregisterRequirement(String type) { return this.requirementBuilderMap.remove(type) != null; } + /** + * Retrieves a ConditionalElement from a given ConfigurationSection. + * + * @param section The ConfigurationSection containing the conditional element data. + * @return A ConditionalElement instance representing the data in the section. + */ + private ConditionalElement getConditionalElements(ConfigurationSection section) { + var sub = section.getConfigurationSection("sub-groups"); + if (sub == null) { + return new ConditionalElement( + getRequirements(section.getConfigurationSection("conditions"), false), + ConfigUtils.getModifiers(section.getStringList("list")), + null + ); + } else { + HashMap subElements = new HashMap<>(); + for (Map.Entry entry : sub.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + subElements.put(entry.getKey(), getConditionalElements(innerSection)); + } + } + return new ConditionalElement( + getRequirements(section.getConfigurationSection("conditions"), false), + ConfigUtils.getModifiers(section.getStringList("list")), + subElements + ); + } + } + private void registerInbuiltRequirements() { this.registerTimeRequirement(); this.registerYRequirement(); @@ -149,39 +197,21 @@ public class RequirementManagerImpl implements RequirementManager { this.registerHookRequirement(); } - public ConditionalElement getConditionalElements(ConfigurationSection section) { - var sub = section.getConfigurationSection("sub-groups"); - if (sub == null) { - return new ConditionalElement( - getRequirements(section.getConfigurationSection("conditions"), false), - ConfigUtils.getModifiers(section.getStringList("list")), - null - ); - } else { - HashMap subElements = new HashMap<>(); - for (Map.Entry entry : sub.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - subElements.put(entry.getKey(), getConditionalElements(innerSection)); - } - } - return new ConditionalElement( - getRequirements(section.getConfigurationSection("conditions"), false), - ConfigUtils.getModifiers(section.getStringList("list")), - subElements - ); - } - } - - @Override public HashMap getLootWithWeight(Condition condition) { return getString2DoubleMap(condition, conditionalLootsMap); } - @Override public HashMap getGameWithWeight(Condition condition) { return getString2DoubleMap(condition, conditionalGamesMap); } + /** + * Retrieves a mapping of strings to doubles based on conditional elements and a player's condition. + * + * @param condition The player's condition. + * @param conditionalGamesMap The map of conditional elements representing loots/games. + * @return A HashMap with strings as keys and doubles as values representing loot/game weights. + */ @NotNull private HashMap getString2DoubleMap(Condition condition, LinkedHashMap conditionalGamesMap) { HashMap lootWeightMap = new HashMap<>(); @@ -191,10 +221,10 @@ public class RequirementManagerImpl implements RequirementManager { while (!lootQueue.isEmpty()) { HashMap currentLootMap = lootQueue.poll(); for (ConditionalElement loots : currentLootMap.values()) { - if (loots.isConditionsMet(condition)) { + if (RequirementManager.isRequirementMet(condition, loots.getRequirements())) { loots.combine(player, lootWeightMap); - if (loots.getSubLoots() != null) { - lootQueue.add(loots.getSubLoots()); + if (loots.getSubElements() != null) { + lootQueue.add(loots.getSubElements()); } } } @@ -202,15 +232,24 @@ public class RequirementManagerImpl implements RequirementManager { return lootWeightMap; } - @Nullable + /** + * Retrieves an array of requirements based on a configuration section. + * + * @param section The configuration section containing requirement definitions. + * @param advanced A flag indicating whether to use advanced requirements. + * @return An array of Requirement objects based on the configuration section + */ + @NotNull @Override public Requirement[] getRequirements(ConfigurationSection section, boolean advanced) { - if (section == null) return null; List requirements = new ArrayList<>(); + if (section == null) { + return requirements.toArray(new Requirement[0]); + } for (Map.Entry entry : section.getValues(false).entrySet()) { String typeOrName = entry.getKey(); if (hasRequirement(typeOrName)) { - requirements.add(getRequirementBuilder(typeOrName).build(entry.getValue(), null, advanced)); + requirements.add(getRequirement(typeOrName, entry.getValue())); } else { requirements.add(getRequirement(section.getConfigurationSection(typeOrName), advanced)); } @@ -222,6 +261,13 @@ public class RequirementManagerImpl implements RequirementManager { return requirementBuilderMap.containsKey(type); } + /** + * Retrieves a Requirement object based on a configuration section and advanced flag. + * + * @param section The configuration section containing requirement definitions. + * @param advanced A flag indicating whether to use advanced requirements. + * @return A Requirement object based on the configuration section, or an EmptyRequirement if the section is null or invalid. + */ @NotNull @Override public Requirement getRequirement(ConfigurationSection section, boolean advanced) { @@ -244,31 +290,48 @@ public class RequirementManagerImpl implements RequirementManager { LogUtils.warn("No requirement type found at " + section.getCurrentPath()); return EmptyRequirement.instance; } - var builder = getRequirementBuilder(type); + var builder = getRequirementFactory(type); if (builder == null) { return EmptyRequirement.instance; } return builder.build(section.get("value"), actionList, advanced); } + /** + * Gets a requirement based on the provided key and value. + * If a valid RequirementFactory is found for the key, it is used to create the requirement. + * If no factory is found, a warning is logged, and an empty requirement instance is returned. + * + * @param type The key representing the requirement type. + * @param value The value associated with the requirement. + * @return A Requirement instance based on the key and value, or an empty requirement if not found. + */ @Override - public Requirement getRequirement(String key, Object value) { - return getRequirementBuilder(key).build(value); - } - - private Pair getIntegerPair(String range) { - String[] split = range.split("~"); - return Pair.of(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + @NotNull + public Requirement getRequirement(String type, Object value) { + RequirementFactory factory = getRequirementFactory(type); + if (factory == null) { + LogUtils.warn("Requirement type: " + type + " doesn't exist."); + return EmptyRequirement.instance; + } + return factory.build(value); } + /** + * Retrieves a RequirementFactory based on the specified requirement type. + * + * @param type The requirement type for which to retrieve a factory. + * @return A RequirementFactory for the specified type, or null if no factory is found. + */ @Override - public RequirementFactory getRequirementBuilder(String type) { + @Nullable + public RequirementFactory getRequirementFactory(String type) { return requirementBuilderMap.get(type); } private void registerTimeRequirement() { registerRequirement("time", (args, actions, advanced) -> { - List> timePairs = ConfigUtils.stringListArgs(args).stream().map(this::getIntegerPair).toList(); + List> timePairs = ConfigUtils.stringListArgs(args).stream().map(ConfigUtils::splitStringIntegerArgs).toList(); return condition -> { long time = condition.getLocation().getWorld().getTime(); for (Pair pair : timePairs) @@ -322,7 +385,7 @@ public class RequirementManagerImpl implements RequirementManager { }); } - @SuppressWarnings("all") + @SuppressWarnings("unchecked") private void registerLootRequirement() { registerRequirement("loot", (args, actions, advanced) -> { List arg = (List) args; @@ -346,7 +409,7 @@ public class RequirementManagerImpl implements RequirementManager { private void registerYRequirement() { registerRequirement("ypos", (args, actions, advanced) -> { - List> timePairs = ConfigUtils.stringListArgs(args).stream().map(this::getIntegerPair).toList(); + List> timePairs = ConfigUtils.stringListArgs(args).stream().map(ConfigUtils::splitStringIntegerArgs).toList(); return condition -> { int y = condition.getLocation().getBlockY(); for (Pair pair : timePairs) @@ -363,7 +426,6 @@ public class RequirementManagerImpl implements RequirementManager { if (args instanceof ConfigurationSection section) { Requirement[] requirements = getRequirements(section, advanced); return condition -> { - if (requirements == null) return true; for (Requirement requirement : requirements) { if (requirement.isConditionMet(condition)) { return true; @@ -384,7 +446,6 @@ public class RequirementManagerImpl implements RequirementManager { if (args instanceof ConfigurationSection section) { Requirement[] requirements = getRequirements(section, advanced); return condition -> { - if (requirements == null) return true; outer: { for (Requirement requirement : requirements) { if (!requirement.isConditionMet(condition)) { @@ -626,6 +687,7 @@ public class RequirementManagerImpl implements RequirementManager { }); } + @SuppressWarnings("DuplicatedCode") private void registerGreaterThanRequirement() { registerRequirement(">=", (args, actions, advanced) -> { if (args instanceof ConfigurationSection section) { @@ -713,6 +775,7 @@ public class RequirementManagerImpl implements RequirementManager { }); } + @SuppressWarnings("DuplicatedCode") private void registerLessThanRequirement() { registerRequirement("<", (args, actions, advanced) -> { if (args instanceof ConfigurationSection section) { @@ -919,7 +982,7 @@ public class RequirementManagerImpl implements RequirementManager { ItemStack itemStack = mainOrOff ? condition.getPlayer().getInventory().getItemInMainHand() : condition.getPlayer().getInventory().getItemInOffHand(); - String id = plugin.getItemManager().getAnyItemID(itemStack); + String id = plugin.getItemManager().getAnyPluginItemID(itemStack); if (items.contains(id) && itemStack.getAmount() >= amount) return true; if (advanced) triggerActions(actions, condition); return false; @@ -993,7 +1056,7 @@ public class RequirementManagerImpl implements RequirementManager { int level = section.getInt("level"); String target = section.getString("target"); return condition -> { - LevelInterface levelInterface = plugin.getIntegrationManager().getLevelHook(pluginName); + LevelInterface levelInterface = plugin.getIntegrationManager().getLevelPlugin(pluginName); if (levelInterface == null) { LogUtils.warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation."); return true; @@ -1010,12 +1073,25 @@ public class RequirementManagerImpl implements RequirementManager { }); } + /** + * Triggers a list of actions with the given condition. + * If the list of actions is not null, each action in the list is triggered. + * + * @param actions The list of actions to trigger. + * @param condition The condition associated with the actions. + */ private void triggerActions(List actions, Condition condition) { if (actions != null) for (Action action : actions) action.trigger(condition); } + /** + * Loads requirement expansions from external JAR files located in the expansion folder. + * Each expansion JAR should contain classes that extends the RequirementExpansion class. + * Expansions are registered and used to create custom requirements. + * If an error occurs while loading or initializing an expansion, a warning message is logged. + */ @SuppressWarnings("ResultOfMethodCallIgnored") private void loadExpansions() { File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/statistic/StatisticsManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/statistic/StatisticsManagerImpl.java index ed8833cb..732dd3bf 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/statistic/StatisticsManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/statistic/StatisticsManagerImpl.java @@ -49,7 +49,14 @@ public class StatisticsManagerImpl implements StatisticsManager { unload(); } + /** + * Get the statistics for a player with the given UUID. + * + * @param uuid The UUID of the player for whom statistics are retrieved. + * @return The player's statistics or null if the player is not found. + */ @Override + @Nullable public Statistics getStatistics(UUID uuid) { OnlineUser onlineUser = plugin.getStorageManager().getOnlineUser(uuid); if (onlineUser == null) return null; @@ -88,6 +95,12 @@ public class StatisticsManagerImpl implements StatisticsManager { } } + /** + * Get a list of strings associated with a specific key in a category map. + * + * @param key The key to look up in the category map. + * @return A list of strings associated with the key or null if the key is not found. + */ @Override @Nullable public List getCategory(String key) { diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/ActivatedTotem.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/ActivatedTotem.java index 435a1e6b..1e9f3e04 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/ActivatedTotem.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/ActivatedTotem.java @@ -43,7 +43,7 @@ public class ActivatedTotem { this.expireTime = System.currentTimeMillis() + config.getDuration() * 1000L; this.coreLocation = coreLocation.clone().add(0.5,0,0.5); this.totemConfig = config; - this.effectCarrier = CustomFishingPlugin.get().getEffectManager().getEffect("totem", config.getKey()); + this.effectCarrier = CustomFishingPlugin.get().getEffectManager().getEffectCarrier("totem", config.getKey()); for (ParticleSetting particleSetting : config.getParticleSettings()) { this.subTasks.add(particleSetting.start(coreLocation, config.getRadius())); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/TotemManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/TotemManagerImpl.java index 3d8c789f..349c838f 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/TotemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/TotemManagerImpl.java @@ -105,6 +105,12 @@ public class TotemManagerImpl implements TotemManager, Listener { unload(); } + /** + * Get the EffectCarrier associated with an activated totem located near the specified location. + * + * @param location The location to search for activated totems. + * @return The EffectCarrier associated with the nearest activated totem or null if none are found. + */ @Override @Nullable public EffectCarrier getTotemEffect(Location location) { @@ -139,7 +145,7 @@ public class TotemManagerImpl implements TotemManager, Listener { return; Block block = event.getClickedBlock(); assert block != null; - String id = plugin.getBlockManager().getAnyBlockID(block); + String id = plugin.getBlockManager().getAnyPluginBlockID(block); List configs = totemConfigMap.get(id); if (configs == null) return; @@ -153,7 +159,7 @@ public class TotemManagerImpl implements TotemManager, Listener { if (config == null) return; String totemKey = config.getKey(); - EffectCarrier carrier = plugin.getEffectManager().getEffect("totem", totemKey); + EffectCarrier carrier = plugin.getEffectManager().getEffectCarrier("totem", totemKey); if (carrier == null) return; Condition condition = new Condition(block.getLocation(), event.getPlayer(), new HashMap<>()); diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/block/type/EqualType.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/block/type/EqualType.java index 56c3ff40..8ba89a25 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/block/type/EqualType.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/block/type/EqualType.java @@ -32,7 +32,7 @@ public class EqualType implements TypeCondition, Serializable { @Override public boolean isMet(Block type) { - return this.type.equals(CustomFishingPlugin.get().getBlockManager().getAnyBlockID(type)); + return this.type.equals(CustomFishingPlugin.get().getBlockManager().getAnyPluginBlockID(type)); } @Override diff --git a/plugin/src/main/java/net/momirealms/customfishing/scheduler/BukkitSchedulerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/scheduler/BukkitSchedulerImpl.java index 2caeef3b..f8cf674a 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/scheduler/BukkitSchedulerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/scheduler/BukkitSchedulerImpl.java @@ -23,6 +23,9 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.scheduler.BukkitTask; +/** + * A scheduler implementation for synchronous tasks using Bukkit's Scheduler. + */ public class BukkitSchedulerImpl implements SyncScheduler { private final CustomFishingPlugin plugin; @@ -31,6 +34,13 @@ public class BukkitSchedulerImpl implements SyncScheduler { this.plugin = plugin; } + /** + * Runs a synchronous task on the main server thread using Bukkit's Scheduler. + * If already on the main thread, the task is executed immediately. + * + * @param runnable The task to run. + * @param location The location associated with the task. + */ @Override public void runSyncTask(Runnable runnable, Location location) { if (Bukkit.isPrimaryThread()) @@ -39,16 +49,36 @@ public class BukkitSchedulerImpl implements SyncScheduler { Bukkit.getScheduler().runTask(plugin, runnable); } + /** + * Runs a synchronous task repeatedly with a specified delay and period using Bukkit's Scheduler. + * + * @param runnable The task to run. + * @param location The location associated with the task. + * @param delay The delay in ticks before the first execution. + * @param period The period between subsequent executions in ticks. + * @return A CancellableTask for managing the scheduled task. + */ @Override public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period) { return new BukkitCancellableTask(Bukkit.getScheduler().runTaskTimer(plugin, runnable, delay, period)); } + /** + * Runs a synchronous task with a specified delay using Bukkit's Scheduler. + * + * @param runnable The task to run. + * @param location The location associated with the task. + * @param delay The delay in ticks before the task execution. + * @return A CancellableTask for managing the scheduled task. + */ @Override public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) { return new BukkitCancellableTask(Bukkit.getScheduler().runTaskLater(plugin, runnable, delay)); } + /** + * Represents a scheduled task using Bukkit's Scheduler that can be cancelled. + */ public static class BukkitCancellableTask implements CancellableTask { private final BukkitTask bukkitTask; diff --git a/plugin/src/main/java/net/momirealms/customfishing/scheduler/FoliaSchedulerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/scheduler/FoliaSchedulerImpl.java index 9f361a88..9980431c 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/scheduler/FoliaSchedulerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/scheduler/FoliaSchedulerImpl.java @@ -23,6 +23,9 @@ import net.momirealms.customfishing.api.scheduler.CancellableTask; import org.bukkit.Bukkit; import org.bukkit.Location; +/** + * A scheduler implementation for "synchronous" tasks using Folia's RegionScheduler. + */ public class FoliaSchedulerImpl implements SyncScheduler { private final CustomFishingPlugin plugin; @@ -31,21 +34,47 @@ public class FoliaSchedulerImpl implements SyncScheduler { this.plugin = plugin; } + /** + * Runs a "synchronous" task on the region thread using Folia's RegionScheduler. + * + * @param runnable The task to run. + * @param location The location associated with the task. + */ @Override public void runSyncTask(Runnable runnable, Location location) { Bukkit.getRegionScheduler().execute(plugin, location, runnable); } + /** + * Runs a "synchronous" task repeatedly with a specified delay and period using Folia's RegionScheduler. + * + * @param runnable The task to run. + * @param location The location associated with the task. + * @param delay The delay in ticks before the first execution. + * @param period The period between subsequent executions in ticks. + * @return A CancellableTask for managing the scheduled task. + */ @Override public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period) { return new FoliaCancellableTask(Bukkit.getRegionScheduler().runAtFixedRate(plugin, location, (scheduledTask -> runnable.run()), delay, period)); } + /** + * Runs a "synchronous" task with a specified delay using Folia's RegionScheduler. + * + * @param runnable The task to run. + * @param location The location associated with the task. + * @param delay The delay in ticks before the task execution. + * @return A CancellableTask for managing the scheduled task. + */ @Override public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) { return new FoliaCancellableTask(Bukkit.getRegionScheduler().runDelayed(plugin, location, (scheduledTask -> runnable.run()), delay)); } + /** + * Represents a scheduled task using Folia's RegionScheduler that can be cancelled. + */ public static class FoliaCancellableTask implements CancellableTask { private final ScheduledTask scheduledTask; diff --git a/plugin/src/main/java/net/momirealms/customfishing/scheduler/SchedulerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/scheduler/SchedulerImpl.java index ae3186b8..b929a308 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/scheduler/SchedulerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/scheduler/SchedulerImpl.java @@ -28,6 +28,9 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +/** + * A scheduler implementation responsible for scheduling and managing tasks in a multi-threaded environment. + */ public class SchedulerImpl implements Scheduler { private final SyncScheduler syncScheduler; @@ -44,37 +47,80 @@ public class SchedulerImpl implements Scheduler { this.schedule.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); } + /** + * Reloads the scheduler configuration based on CustomFishingPlugin settings. + */ public void reload() { this.schedule.setCorePoolSize(CFConfig.corePoolSize); this.schedule.setKeepAliveTime(CFConfig.keepAliveTime, TimeUnit.SECONDS); this.schedule.setMaximumPoolSize(CFConfig.maximumPoolSize); } + /** + * Shuts down the scheduler. + */ public void shutdown() { if (this.schedule != null && !this.schedule.isShutdown()) this.schedule.shutdown(); } + /** + * Runs a task synchronously on the main server thread or region thread. + * + * @param runnable The task to run. + * @param location The location associated with the task. + */ @Override public void runTaskSync(Runnable runnable, Location location) { this.syncScheduler.runSyncTask(runnable, location); } + /** + * Runs a task asynchronously. + * + * @param runnable The task to run. + */ @Override public void runTaskAsync(Runnable runnable) { this.schedule.execute(runnable); } + /** + * Runs a task synchronously with a specified delay and period. + * + * @param runnable The task to run. + * @param location The location associated with the task. + * @param delayTicks The delay in ticks before the first execution. + * @param periodTicks The period between subsequent executions in ticks. + * @return A CancellableTask for managing the scheduled task. + */ @Override public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delayTicks, long periodTicks) { return this.syncScheduler.runTaskSyncTimer(runnable, location, delayTicks, periodTicks); } + /** + * Runs a task asynchronously with a specified delay. + * + * @param runnable The task to run. + * @param delay The delay before the task execution. + * @param timeUnit The time unit for the delay. + * @return A CancellableTask for managing the scheduled task. + */ @Override public CancellableTask runTaskAsyncLater(Runnable runnable, long delay, TimeUnit timeUnit) { return new ScheduledTask(schedule.schedule(runnable, delay, timeUnit)); } + /** + * Runs a task synchronously with a specified delay. + * + * @param runnable The task to run. + * @param location The location associated with the task. + * @param delay The delay before the task execution. + * @param timeUnit The time unit for the delay. + * @return A CancellableTask for managing the scheduled task. + */ @Override public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay, TimeUnit timeUnit) { return new ScheduledTask(schedule.schedule(() -> { @@ -82,16 +128,36 @@ public class SchedulerImpl implements Scheduler { }, delay, timeUnit)); } + /** + * Runs a task synchronously with a specified delay in ticks. + * + * @param runnable The task to run. + * @param location The location associated with the task. + * @param delayTicks The delay in ticks before the task execution. + * @return A CancellableTask for managing the scheduled task. + */ @Override public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delayTicks) { return this.syncScheduler.runTaskSyncLater(runnable, location, delayTicks); } + /** + * Runs a task asynchronously with a specified delay and period. + * + * @param runnable The task to run. + * @param delay The delay before the first execution. + * @param period The period between subsequent executions. + * @param timeUnit The time unit for the delay and period. + * @return A CancellableTask for managing the scheduled task. + */ @Override public CancellableTask runTaskAsyncTimer(Runnable runnable, long delay, long period, TimeUnit timeUnit) { return new ScheduledTask(schedule.scheduleAtFixedRate(runnable, delay, period, timeUnit)); } + /** + * Represents a thread-pool task that can be cancelled. + */ public static class ScheduledTask implements CancellableTask { private final ScheduledFuture scheduledFuture; diff --git a/plugin/src/main/java/net/momirealms/customfishing/scheduler/SyncScheduler.java b/plugin/src/main/java/net/momirealms/customfishing/scheduler/SyncScheduler.java index 4392fbef..9d706dc1 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/scheduler/SyncScheduler.java +++ b/plugin/src/main/java/net/momirealms/customfishing/scheduler/SyncScheduler.java @@ -22,9 +22,32 @@ import org.bukkit.Location; public interface SyncScheduler { + /** + * Runs a task synchronously on the main server thread or region thread. + * + * @param runnable The task to run. + * @param location The location associated with the task. + */ void runSyncTask(Runnable runnable, Location location); - CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period); + /** + * Runs a task synchronously with a specified delay and period. + * + * @param runnable The task to run. + * @param location The location associated with the task. + * @param delayTicks The delay in ticks before the first execution. + * @param periodTicks The period between subsequent executions in ticks. + * @return A CancellableTask for managing the scheduled task. + */ + CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delayTicks, long periodTicks); - CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay); + /** + * Runs a task synchronously with a specified delay in ticks. + * + * @param runnable The task to run. + * @param location The location associated with the task. + * @param delayTicks The delay in ticks before the task execution. + * @return A CancellableTask for managing the scheduled task. + */ + CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delayTicks); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/StorageManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/StorageManagerImpl.java index 3a4edbf5..11d92dc3 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/StorageManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/StorageManagerImpl.java @@ -60,6 +60,10 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +/** + * This class implements the StorageManager interface and is responsible for managing player data storage. + * It includes methods to handle player data retrieval, storage, and serialization. + */ public class StorageManagerImpl implements StorageManager, Listener { private final CustomFishingPlugin plugin; @@ -81,9 +85,14 @@ public class StorageManagerImpl implements StorageManager, Listener { Bukkit.getPluginManager().registerEvents(this, plugin); } + /** + * Reloads the storage manager configuration. + */ public void reload() { YamlConfiguration config = plugin.getConfig("database.yml"); this.uniqueID = config.getString("unique-server-id", "default"); + + // Check if storage type has changed and reinitialize if necessary StorageType storageType = StorageType.valueOf(config.getString("data-storage-method", "H2")); if (storageType != previousType) { if (this.dataSource != null) this.dataSource.disable(); @@ -100,19 +109,27 @@ public class StorageManagerImpl implements StorageManager, Listener { if (this.dataSource != null) this.dataSource.initialize(); else LogUtils.severe("No storage type is set."); } + + // Handle Redis configuration if (!this.hasRedis && config.getBoolean("Redis.enable", false)) { this.hasRedis = true; this.redisManager = new RedisManager(plugin); this.redisManager.initialize(); } + + // Disable Redis if it was enabled but is now disabled if (this.hasRedis && !config.getBoolean("Redis.enable", false) && this.redisManager != null) { this.redisManager.disable(); this.redisManager = null; } + + // Cancel any existing timerSaveTask if (this.timerSaveTask != null && !this.timerSaveTask.isCancelled()) { this.timerSaveTask.cancel(); } - if (CFConfig.dataSaveInterval != -1) + + // Schedule periodic data saving if dataSaveInterval is configured + if (CFConfig.dataSaveInterval != -1 && CFConfig.dataSaveInterval != 0) this.timerSaveTask = this.plugin.getScheduler().runTaskAsyncTimer( () -> { long time1 = System.currentTimeMillis(); @@ -125,6 +142,9 @@ public class StorageManagerImpl implements StorageManager, Listener { ); } + /** + * Disables the storage manager and cleans up resources. + */ public void disable() { HandlerList.unregisterAll(this); this.dataSource.updateManyPlayersData(onlineUserMap.values(), true); @@ -135,16 +155,35 @@ public class StorageManagerImpl implements StorageManager, Listener { this.redisManager.disable(); } + /** + * Gets the unique server identifier. + * + * @return The unique server identifier. + */ + @NotNull @Override public String getUniqueID() { return uniqueID; } + /** + * Gets an OnlineUser instance for the specified UUID. + * + * @param uuid The UUID of the player. + * @return An OnlineUser instance if the player is online, or null if not. + */ @Override public OnlineUser getOnlineUser(UUID uuid) { return onlineUserMap.get(uuid); } + /** + * Asynchronously retrieves an OfflineUser instance for the specified UUID. + * + * @param uuid The UUID of the player. + * @param lock Whether to lock the data during retrieval. + * @return A CompletableFuture that resolves to an Optional containing the OfflineUser instance if found, or empty if not found or locked. + */ @Override public CompletableFuture> getOfflineUser(UUID uuid, boolean lock) { var optionalDataFuture = dataSource.getPlayerData(uuid, lock); @@ -163,21 +202,38 @@ public class StorageManagerImpl implements StorageManager, Listener { }); } + @Override + public boolean isLockedData(OfflineUser offlineUser) { + return OfflineUserImpl.LOCKED_USER == offlineUser; + } + + /** + * Asynchronously saves user data for an OfflineUser. + * + * @param offlineUser The OfflineUser whose data needs to be saved. + * @param unlock Whether to unlock the data after saving. + * @return A CompletableFuture that resolves to a boolean indicating the success of the data saving operation. + */ @Override public CompletableFuture saveUserData(OfflineUser offlineUser, boolean unlock) { return dataSource.updatePlayerData(offlineUser.getUUID(), offlineUser.getPlayerData(), unlock); } - @Override - public CompletableFuture getRedisPlayerCount() { - return redisManager.getPlayerCount(); - } - + /** + * Gets the data source used for data storage. + * + * @return The data source. + */ @Override public DataStorageInterface getDataSource() { return dataSource; } + /** + * Event handler for when a player joins the server. + * Locks the player's data and initiates data retrieval if Redis is not used, + * otherwise, it starts a Redis data retrieval task. + */ @EventHandler public void onJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); @@ -186,10 +242,21 @@ public class StorageManagerImpl implements StorageManager, Listener { if (!hasRedis) { waitForDataLockRelease(uuid, 1); } else { - redisReadingData(uuid); + plugin.getScheduler().runTaskAsyncLater(() -> redisManager.getChangeServer(uuid).thenAccept(changeServer -> { + if (!changeServer) { + waitForDataLockRelease(uuid, 3); + } else { + new RedisGetDataTask(uuid); + } + }), 500, TimeUnit.MILLISECONDS); } } + /** + * Event handler for when a player quits the server. + * If the player is not locked, it removes their OnlineUser instance, + * updates the player's data in Redis and the data source. + */ @EventHandler public void onQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); @@ -216,17 +283,10 @@ public class StorageManagerImpl implements StorageManager, Listener { } } - public void redisReadingData(UUID uuid) { - // delay 0.5s for another server to insert the key - plugin.getScheduler().runTaskAsyncLater(() -> redisManager.getChangeServer(uuid).thenAccept(changeServer -> { - if (!changeServer) { - waitForDataLockRelease(uuid, 3); - } else { - new RedisGetDataTask(uuid); - } - }), 500, TimeUnit.MILLISECONDS); - } - + /** + * Runnable task for asynchronously retrieving data from Redis. + * Retries up to 6 times and cancels the task if the player is offline. + */ public class RedisGetDataTask implements Runnable { private final UUID uuid; @@ -255,21 +315,25 @@ public class StorageManagerImpl implements StorageManager, Listener { if (optionalData.isPresent()) { putDataInCache(player, optionalData.get()); task.cancel(); - if (CFConfig.lockData) dataSource.lockPlayerData(uuid, true); + if (CFConfig.lockData) dataSource.lockOrUnlockPlayerData(uuid, true); } }); } } - // wait 1 second for the lock to release - // try three times at most + /** + * Waits for data lock release with a delay and a maximum of three retries. + * + * @param uuid The UUID of the player. + * @param times The number of times this method has been retried. + */ public void waitForDataLockRelease(UUID uuid, int times) { plugin.getScheduler().runTaskAsyncLater(() -> { var player = Bukkit.getPlayer(uuid); if (player == null || !player.isOnline() || times > 3) return; this.dataSource.getPlayerData(uuid, CFConfig.lockData).thenAccept(optionalData -> { - // should not be empty + // Data should not be empty if (optionalData.isEmpty()) return; if (optionalData.get() == PlayerData.LOCKED) { @@ -281,38 +345,80 @@ public class StorageManagerImpl implements StorageManager, Listener { }, 1, TimeUnit.SECONDS); } + /** + * Puts player data in cache and removes the player from the locked set. + * + * @param player The player whose data is being cached. + * @param playerData The data to be cached. + */ public void putDataInCache(Player player, PlayerData playerData) { locked.remove(player.getUniqueId()); OnlineUserImpl bukkitUser = new OnlineUserImpl(player, playerData); onlineUserMap.put(player.getUniqueId(), bukkitUser); } + /** + * Checks if Redis is enabled. + * + * @return True if Redis is enabled; otherwise, false. + */ @Override public boolean isRedisEnabled() { return hasRedis; } + /** + * Gets the RedisManager instance. + * + * @return The RedisManager instance. + */ @Nullable public RedisManager getRedisManager() { return redisManager; } + /** + * Converts PlayerData to bytes. + * + * @param data The PlayerData to be converted. + * @return The byte array representation of PlayerData. + */ + @NotNull @Override public byte[] toBytes(@NotNull PlayerData data) { return toJson(data).getBytes(StandardCharsets.UTF_8); } + /** + * Converts PlayerData to JSON format. + * + * @param data The PlayerData to be converted. + * @return The JSON string representation of PlayerData. + */ @Override @NotNull public String toJson(@NotNull PlayerData data) { return gson.toJson(data); } + /** + * Converts JSON string to PlayerData. + * + * @param json The JSON string to be converted. + * @return The PlayerData object. + */ + @NotNull @Override public PlayerData fromJson(String json) { return gson.fromJson(json, PlayerData.class); } + /** + * Converts bytes to PlayerData. + * + * @param data The byte array to be converted. + * @return The PlayerData object. + */ @Override @NotNull public PlayerData fromBytes(byte[] data) { @@ -323,6 +429,9 @@ public class StorageManagerImpl implements StorageManager, Listener { } } + /** + * Custom exception class for data serialization errors. + */ public static class DataSerializationException extends RuntimeException { protected DataSerializationException(String message, Throwable cause) { super(message, cause); diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/AbstractStorage.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/AbstractStorage.java index b840afe7..d9306d06 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/AbstractStorage.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/AbstractStorage.java @@ -27,6 +27,9 @@ import java.util.Collection; import java.util.UUID; import java.util.concurrent.CompletableFuture; +/** + * An abstract class that implements the DataStorageInterface and provides common functionality for data storage. + */ public abstract class AbstractStorage implements DataStorageInterface { protected CustomFishingPlugin plugin; @@ -37,31 +40,44 @@ public abstract class AbstractStorage implements DataStorageInterface { @Override public void initialize() { - + // This method can be overridden in subclasses to perform initialization tasks specific to the storage type. } @Override public void disable() { - + // This method can be overridden in subclasses to perform cleanup or shutdown tasks specific to the storage type. } + /** + * Get the current time in seconds since the Unix epoch. + * + * @return The current time in seconds. + */ public int getCurrentSeconds() { return (int) Instant.now().getEpochSecond(); } @Override public void updateManyPlayersData(Collection users, boolean unlock) { + // Update data for multiple players by iterating through the collection of OfflineUser objects. for (OfflineUser user : users) { this.updatePlayerData(user.getUUID(), user.getPlayerData(), unlock); } } - public void lockPlayerData(UUID uuid, boolean lock) { - + /** + * Lock or unlock player data based on the provided UUID and lock flag. + * + * @param uuid The UUID of the player. + * @param lock True to lock the player data, false to unlock it. + */ + public void lockOrUnlockPlayerData(UUID uuid, boolean lock) { + // Note: Only remote database would override this method } @Override public CompletableFuture updateOrInsertPlayerData(UUID uuid, PlayerData playerData, boolean unlock) { + // By default, delegate to the updatePlayerData method to update or insert player data. return updatePlayerData(uuid, playerData, unlock); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/MongoDBImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/MongoDBImpl.java index 3ea7b3ce..1bfd7562 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/MongoDBImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/MongoDBImpl.java @@ -39,6 +39,9 @@ import org.bukkit.configuration.file.YamlConfiguration; import java.util.*; import java.util.concurrent.CompletableFuture; +/** + * An implementation of AbstractStorage that uses MongoDB for player data storage. + */ public class MongoDBImpl extends AbstractStorage { private MongoClient mongoClient; @@ -49,6 +52,9 @@ public class MongoDBImpl extends AbstractStorage { super(plugin); } + /** + * Initialize the MongoDB connection and configuration based on the plugin's YAML configuration. + */ @Override public void initialize() { YamlConfiguration config = plugin.getConfig("database.yml"); @@ -83,6 +89,9 @@ public class MongoDBImpl extends AbstractStorage { this.database = mongoClient.getDatabase(section.getString("database", "minecraft")); } + /** + * Disable the MongoDB connection by closing the MongoClient. + */ @Override public void disable() { if (this.mongoClient != null) { @@ -90,10 +99,21 @@ public class MongoDBImpl extends AbstractStorage { } } - public String getCollectionName(String sub) { - return getCollectionPrefix() + "_" + sub; + /** + * Get the collection name for a specific subcategory of data. + * + * @param value The subcategory identifier. + * @return The full collection name including the prefix. + */ + public String getCollectionName(String value) { + return getCollectionPrefix() + "_" + value; } + /** + * Get the collection prefix used for MongoDB collections. + * + * @return The collection prefix. + */ public String getCollectionPrefix() { return collectionPrefix; } @@ -103,6 +123,13 @@ public class MongoDBImpl extends AbstractStorage { return StorageType.MongoDB; } + /** + * Asynchronously retrieve player data from the MongoDB database. + * + * @param uuid The UUID of the player. + * @param lock Flag indicating whether to lock the data. + * @return A CompletableFuture with an optional PlayerData. + */ @Override public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { var future = new CompletableFuture>(); @@ -111,7 +138,7 @@ public class MongoDBImpl extends AbstractStorage { Document doc = collection.find(Filters.eq("uuid", uuid)).first(); if (doc == null) { if (Bukkit.getPlayer(uuid) != null) { - if (lock) lockPlayerData(uuid, true); + if (lock) lockOrUnlockPlayerData(uuid, true); future.complete(Optional.of(PlayerData.empty())); } else { future.complete(Optional.empty()); @@ -122,13 +149,21 @@ public class MongoDBImpl extends AbstractStorage { return; } Binary binary = (Binary) doc.get("data"); - if (lock) lockPlayerData(uuid, true); + if (lock) lockOrUnlockPlayerData(uuid, true); future.complete(Optional.of(plugin.getStorageManager().fromBytes(binary.getData()))); } }); return future; } + /** + * Asynchronously update player data in the MongoDB database. + * + * @param uuid The UUID of the player. + * @param playerData The player's data to update. + * @param unlock Flag indicating whether to unlock the data. + * @return A CompletableFuture indicating the update result. + */ @Override public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData, boolean unlock) { var future = new CompletableFuture(); @@ -149,6 +184,12 @@ public class MongoDBImpl extends AbstractStorage { return future; } + /** + * Asynchronously update data for multiple players in the MongoDB database. + * + * @param users A collection of OfflineUser instances to update. + * @param unlock Flag indicating whether to unlock the data. + */ @Override public void updateManyPlayersData(Collection users, boolean unlock) { MongoCollection collection = database.getCollection(getCollectionName("data")); @@ -170,8 +211,14 @@ public class MongoDBImpl extends AbstractStorage { } } + /** + * Lock or unlock player data in the MongoDB database. + * + * @param uuid The UUID of the player. + * @param lock Flag indicating whether to lock or unlock the data. + */ @Override - public void lockPlayerData(UUID uuid, boolean lock) { + public void lockOrUnlockPlayerData(UUID uuid, boolean lock) { MongoCollection collection = database.getCollection(getCollectionName("data")); try { Document query = new Document("uuid", uuid); @@ -183,6 +230,12 @@ public class MongoDBImpl extends AbstractStorage { } } + /** + * Get a set of unique player UUIDs from the MongoDB database. + * + * @param legacy Flag indicating whether to retrieve legacy data. + * @return A set of unique player UUIDs. + */ @Override public Set getUniqueUsers(boolean legacy) { // no legacy files diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/RedisManager.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/RedisManager.java index 009b9ea1..1a7a5531 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/RedisManager.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/RedisManager.java @@ -37,6 +37,9 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +/** + * A RedisManager class responsible for managing interactions with a Redis server for data storage. + */ public class RedisManager extends AbstractStorage { private static RedisManager instance; @@ -44,7 +47,6 @@ public class RedisManager extends AbstractStorage { private String password; private int port; private String host; - private JedisPoolConfig jedisPoolConfig; private boolean useSSL; public RedisManager(CustomFishingPlugin plugin) { @@ -52,14 +54,27 @@ public class RedisManager extends AbstractStorage { instance = this; } + /** + * Get the singleton instance of the RedisManager. + * + * @return The RedisManager instance. + */ public static RedisManager getInstance() { return instance; } + /** + * Get a Jedis resource for interacting with the Redis server. + * + * @return A Jedis resource. + */ public Jedis getJedis() { return jedisPool.getResource(); } + /** + * Initialize the Redis connection and configuration based on the plugin's YAML configuration. + */ @Override public void initialize() { YamlConfiguration config = plugin.getConfig("database.yml"); @@ -69,7 +84,7 @@ public class RedisManager extends AbstractStorage { return; } - jedisPoolConfig = new JedisPoolConfig(); + JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setTestWhileIdle(true); jedisPoolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(30000)); jedisPoolConfig.setNumTestsPerEvictionRun(-1); @@ -99,13 +114,21 @@ public class RedisManager extends AbstractStorage { subscribe(); } + /** + * Disable the Redis connection by closing the JedisPool. + */ @Override public void disable() { - this.removeServerPlayers(plugin.getStorageManager().getUniqueID()); if (jedisPool != null && !jedisPool.isClosed()) jedisPool.close(); } + /** + * Send a message to Redis on a specified channel. + * + * @param channel The Redis channel to send the message to. + * @param message The message to send. + */ public void sendRedisMessage(@NotNull String channel, @NotNull String message) { try (Jedis jedis = jedisPool.getResource()) { jedis.publish(channel, message); @@ -113,6 +136,9 @@ public class RedisManager extends AbstractStorage { } } + /** + * Subscribe to Redis messages on a separate thread and handle received messages. + */ private void subscribe() { Thread thread = new Thread(() -> { try (final Jedis jedis = password.isBlank() ? @@ -160,38 +186,12 @@ public class RedisManager extends AbstractStorage { return StorageType.Redis; } - public CompletableFuture getPlayerCount() { - var future = new CompletableFuture(); - plugin.getScheduler().runTaskAsync(() -> { - int players = 0; - try (Jedis jedis = jedisPool.getResource()) { - var list = jedis.zrangeWithScores("cf_players",0, -1); - for (Tuple tuple : list) { - players += (int) tuple.getScore(); - } - } - future.complete(players); - }); - return future; - } - - public CompletableFuture setServerPlayers(int amount, String unique) { - var future = new CompletableFuture(); - plugin.getScheduler().runTaskAsync(() -> { - try (Jedis jedis = jedisPool.getResource()) { - jedis.zadd("cf_players", amount, unique); - } - future.complete(null); - }); - return future; - } - - public void removeServerPlayers(String unique) { - try (Jedis jedis = jedisPool.getResource()) { - jedis.zrem("cf_players", unique); - } - } - + /** + * Set a "change server" flag for a specified player UUID in Redis. + * + * @param uuid The UUID of the player. + * @return A CompletableFuture indicating the operation's completion. + */ public CompletableFuture setChangeServer(UUID uuid) { var future = new CompletableFuture(); plugin.getScheduler().runTaskAsync(() -> { @@ -208,6 +208,12 @@ public class RedisManager extends AbstractStorage { return future; } + /** + * Get the "change server" flag for a specified player UUID from Redis and remove it. + * + * @param uuid The UUID of the player. + * @return A CompletableFuture with a Boolean indicating whether the flag was set. + */ public CompletableFuture getChangeServer(UUID uuid) { var future = new CompletableFuture(); plugin.getScheduler().runTaskAsync(() -> { @@ -227,6 +233,13 @@ public class RedisManager extends AbstractStorage { return future; } + /** + * Asynchronously retrieve player data from Redis. + * + * @param uuid The UUID of the player. + * @param lock Flag indicating whether to lock the data. + * @return A CompletableFuture with an optional PlayerData. + */ @Override public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { var future = new CompletableFuture>(); @@ -250,6 +263,14 @@ public class RedisManager extends AbstractStorage { return future; } + /** + * Asynchronously update player data in Redis. + * + * @param uuid The UUID of the player. + * @param playerData The player's data to update. + * @param ignore Flag indicating whether to ignore the update (not used). + * @return A CompletableFuture indicating the update result. + */ @Override public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData, boolean ignore) { var future = new CompletableFuture(); @@ -270,11 +291,25 @@ public class RedisManager extends AbstractStorage { return future; } + /** + * Get a set of unique player UUIDs from Redis (Returns an empty set). + * This method is designed for importing and exporting so it would not actually be called. + * + * @param legacy Flag indicating whether to retrieve legacy data (not used). + * @return An empty set of UUIDs. + */ @Override public Set getUniqueUsers(boolean legacy) { return new HashSet<>(); } + /** + * Generate a Redis key for a specified key and UUID. + * + * @param key The key identifier. + * @param uuid The UUID to include in the key. + * @return A byte array representing the Redis key. + */ private byte[] getRedisKey(String key, @NotNull UUID uuid) { return (key + ":" + uuid).getBytes(StandardCharsets.UTF_8); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractHikariDatabase.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractHikariDatabase.java index ba2ec575..d2d383d4 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractHikariDatabase.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractHikariDatabase.java @@ -32,6 +32,9 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; +/** + * An abstract base class for SQL databases using the HikariCP connection pool, which handles player data storage. + */ public abstract class AbstractHikariDatabase extends AbstractSQLDatabase implements LegacyDataStorageInterface { private HikariDataSource dataSource; @@ -58,6 +61,9 @@ public abstract class AbstractHikariDatabase extends AbstractSQLDatabase impleme } } + /** + * Initialize the database connection pool and create tables if they don't exist. + */ @Override public void initialize() { YamlConfiguration config = plugin.getConfig("database.yml"); @@ -109,17 +115,32 @@ public abstract class AbstractHikariDatabase extends AbstractSQLDatabase impleme super.createTableIfNotExist(); } + /** + * Disable the database by closing the connection pool. + */ @Override public void disable() { if (dataSource != null && !dataSource.isClosed()) dataSource.close(); } + /** + * Get a connection to the SQL database from the connection pool. + * + * @return A database connection. + * @throws SQLException If there is an error establishing a connection. + */ @Override public Connection getConnection() throws SQLException { return dataSource.getConnection(); } + /** + * Retrieve legacy player data from the SQL database. + * + * @param uuid The UUID of the player. + * @return A CompletableFuture containing the optional legacy player data. + */ @Override public CompletableFuture> getLegacyPlayerData(UUID uuid) { var future = new CompletableFuture>(); diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractSQLDatabase.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractSQLDatabase.java index 7a08f05b..311e088f 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractSQLDatabase.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractSQLDatabase.java @@ -33,6 +33,9 @@ import java.sql.*; import java.util.*; import java.util.concurrent.CompletableFuture; +/** + * An abstract base class for SQL database implementations that handle player data storage. + */ public abstract class AbstractSQLDatabase extends AbstractStorage { protected String tablePrefix; @@ -41,8 +44,17 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { super(plugin); } + /** + * Get a connection to the SQL database. + * + * @return A database connection. + * @throws SQLException If there is an error establishing a connection. + */ public abstract Connection getConnection() throws SQLException; + /** + * Create tables for storing data if they don't exist in the database. + */ public void createTableIfNotExist() { try (Connection connection = getConnection()) { final String[] databaseSchema = getSchema(getStorageType().name().toLowerCase(Locale.ENGLISH)); @@ -60,23 +72,54 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { } } + /** + * Get the SQL schema from a resource file. + * + * @param fileName The name of the schema file. + * @return An array of SQL statements to create tables. + * @throws IOException If there is an error reading the schema resource. + */ private String[] getSchema(@NotNull String fileName) throws IOException { return replaceSchemaPlaceholder(new String(Objects.requireNonNull(plugin.getResource("schema/" + fileName + ".sql")) .readAllBytes(), StandardCharsets.UTF_8)).split(";"); } + /** + * Replace placeholder values in SQL schema with the table prefix. + * + * @param sql The SQL schema string. + * @return The SQL schema string with placeholders replaced. + */ private String replaceSchemaPlaceholder(@NotNull String sql) { return sql.replace("{prefix}", tablePrefix); } + /** + * Get the name of a database table based on a sub-table name and the table prefix. + * + * @param sub The sub-table name. + * @return The full table name. + */ public String getTableName(String sub) { return getTablePrefix() + "_" + sub; } + /** + * Get the current table prefix. + * + * @return The table prefix. + */ public String getTablePrefix() { return tablePrefix; } + /** + * Retrieve a player's data from the SQL database. + * + * @param uuid The UUID of the player. + * @param lock Whether to lock the player data during retrieval. + * @return A CompletableFuture containing the optional player data. + */ @SuppressWarnings("DuplicatedCode") @Override public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { @@ -98,7 +141,7 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { final Blob blob = rs.getBlob("data"); final byte[] dataByteArray = blob.getBytes(1, (int) blob.length()); blob.free(); - if (lock) lockPlayerData(uuid, true); + if (lock) lockOrUnlockPlayerData(uuid, true); future.complete(Optional.of(plugin.getStorageManager().fromBytes(dataByteArray))); } else if (Bukkit.getPlayer(uuid) != null) { var data = PlayerData.empty(); @@ -115,6 +158,14 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { return future; } + /** + * Update a player's data in the SQL database. + * + * @param uuid The UUID of the player. + * @param playerData The player data to update. + * @param unlock Whether to unlock the player data after updating. + * @return A CompletableFuture indicating the success of the update. + */ @Override public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData, boolean unlock) { var future = new CompletableFuture(); @@ -137,6 +188,12 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { return future; } + /** + * Update data for multiple players in the SQL database. + * + * @param users A collection of OfflineUser objects representing players. + * @param unlock Whether to unlock the player data after updating. + */ @Override public void updateManyPlayersData(Collection users, boolean unlock) { String sql = String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data")); @@ -160,6 +217,13 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { } } + /** + * Insert a new player's data into the SQL database. + * + * @param uuid The UUID of the player. + * @param playerData The player data to insert. + * @param lock Whether to lock the player data upon insertion. + */ public void insertPlayerData(UUID uuid, PlayerData playerData, boolean lock) { try ( Connection connection = getConnection(); @@ -174,8 +238,14 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { } } + /** + * Lock or unlock a player's data in the SQL database. + * + * @param uuid The UUID of the player. + * @param lock Whether to lock or unlock the player data. + */ @Override - public void lockPlayerData(UUID uuid, boolean lock) { + public void lockOrUnlockPlayerData(UUID uuid, boolean lock) { try ( Connection connection = getConnection(); PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_LOCK_BY_UUID, getTableName("data"))) @@ -188,6 +258,14 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { } } + /** + * Update or insert a player's data into the SQL database. + * + * @param uuid The UUID of the player. + * @param playerData The player data to update or insert. + * @param unlock Whether to unlock the player data after updating or inserting. + * @return A CompletableFuture indicating the success of the operation. + */ @Override public CompletableFuture updateOrInsertPlayerData(UUID uuid, PlayerData playerData, boolean unlock) { var future = new CompletableFuture(); @@ -211,6 +289,12 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { return future; } + /** + * Get a set of unique user UUIDs from the SQL database. + * + * @param legacy Whether to include legacy data in the retrieval. + * @return A set of unique user UUIDs. + */ @Override public Set getUniqueUsers(boolean legacy) { Set uuids = new HashSet<>(); @@ -228,6 +312,9 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { return uuids; } + /** + * Constants defining SQL statements used for database operations. + */ public static class SqlConstants { public static final String SQL_SELECT_BY_UUID = "SELECT * FROM `%s` WHERE `uuid` = ?"; public static final String SQL_SELECT_ALL_UUID = "SELECT uuid FROM `%s`"; diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/H2Impl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/H2Impl.java index 4b9e09eb..7095c0f5 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/H2Impl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/H2Impl.java @@ -26,6 +26,9 @@ import java.io.File; import java.sql.Connection; import java.sql.SQLException; +/** + * An implementation of AbstractSQLDatabase that uses the H2 embedded database for player data storage. + */ public class H2Impl extends AbstractSQLDatabase { private JdbcConnectionPool connectionPool; @@ -34,6 +37,9 @@ public class H2Impl extends AbstractSQLDatabase { super(plugin); } + /** + * Initialize the H2 database and connection pool based on the configuration. + */ @Override public void initialize() { YamlConfiguration config = plugin.getConfig("database.yml"); @@ -45,6 +51,9 @@ public class H2Impl extends AbstractSQLDatabase { super.createTableIfNotExist(); } + /** + * Disable the H2 database by disposing of the connection pool. + */ @Override public void disable() { if (connectionPool != null) { diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/SQLiteImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/SQLiteImpl.java index d18dad36..afefb1a4 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/SQLiteImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/SQLiteImpl.java @@ -35,6 +35,9 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; +/** + * An implementation of AbstractSQLDatabase that uses the SQLite database for player data storage. + */ public class SQLiteImpl extends AbstractSQLDatabase { private Connection connection; @@ -44,6 +47,9 @@ public class SQLiteImpl extends AbstractSQLDatabase { super(plugin); } + /** + * Initialize the SQLite database and connection based on the configuration. + */ @Override public void initialize() { YamlConfiguration config = plugin.getConfig("database.yml"); @@ -52,6 +58,9 @@ public class SQLiteImpl extends AbstractSQLDatabase { super.createTableIfNotExist(); } + /** + * Disable the SQLite database by closing the connection. + */ @Override public void disable() { try { @@ -67,6 +76,12 @@ public class SQLiteImpl extends AbstractSQLDatabase { return StorageType.SQLite; } + /** + * Get a connection to the SQLite database. + * + * @return A database connection. + * @throws SQLException If there is an error establishing a connection. + */ @Override public Connection getConnection() throws SQLException { if (connection == null || connection.isClosed()) { @@ -75,6 +90,13 @@ public class SQLiteImpl extends AbstractSQLDatabase { return connection; } + /** + * Asynchronously retrieve player data from the SQLite database. + * + * @param uuid The UUID of the player. + * @param lock Flag indicating whether to lock the data. + * @return A CompletableFuture with an optional PlayerData. + */ @SuppressWarnings("DuplicatedCode") @Override public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { @@ -94,7 +116,7 @@ public class SQLiteImpl extends AbstractSQLDatabase { return; } final byte[] dataByteArray = rs.getBytes("data"); - if (lock) lockPlayerData(uuid, true); + if (lock) lockOrUnlockPlayerData(uuid, true); future.complete(Optional.of(plugin.getStorageManager().fromBytes(dataByteArray))); } else if (Bukkit.getPlayer(uuid) != null) { var data = PlayerData.empty(); @@ -111,6 +133,14 @@ public class SQLiteImpl extends AbstractSQLDatabase { return future; } + /** + * Asynchronously update player data in the SQLite database. + * + * @param uuid The UUID of the player. + * @param playerData The player's data to update. + * @param unlock Flag indicating whether to unlock the data. + * @return A CompletableFuture indicating the update result. + */ @Override public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData, boolean unlock) { var future = new CompletableFuture(); @@ -132,6 +162,12 @@ public class SQLiteImpl extends AbstractSQLDatabase { return future; } + /** + * Asynchronously update data for multiple players in the SQLite database. + * + * @param users A collection of OfflineUser instances to update. + * @param unlock Flag indicating whether to unlock the data. + */ @Override public void updateManyPlayersData(Collection users, boolean unlock) { String sql = String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data")); @@ -155,6 +191,13 @@ public class SQLiteImpl extends AbstractSQLDatabase { } } + /** + * Insert player data into the SQLite database. + * + * @param uuid The UUID of the player. + * @param playerData The player's data to insert. + * @param lock Flag indicating whether to lock the data. + */ @Override public void insertPlayerData(UUID uuid, PlayerData playerData, boolean lock) { try ( @@ -170,6 +213,10 @@ public class SQLiteImpl extends AbstractSQLDatabase { } } + /** + * Set up the connection to the SQLite database. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") private void setConnection() { try { if (!databaseFile.exists()) databaseFile.createNewFile(); diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/JsonImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/JsonImpl.java index 233a852f..9f4b2f7f 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/JsonImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/JsonImpl.java @@ -35,6 +35,9 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +/** + * A data storage implementation that uses JSON files to store player data. + */ public class JsonImpl extends AbstractStorage { @SuppressWarnings("ResultOfMethodCallIgnored") @@ -69,10 +72,22 @@ public class JsonImpl extends AbstractStorage { return CompletableFuture.completedFuture(true); } + /** + * Get the file associated with a player's UUID for storing JSON data. + * + * @param uuid The UUID of the player. + * @return The file for the player's data. + */ public File getPlayerDataFile(UUID uuid) { return new File(plugin.getDataFolder(), "data" + File.separator + uuid + ".json"); } + /** + * Save an object to a JSON file. + * + * @param obj The object to be saved as JSON. + * @param filepath The file path where the JSON file should be saved. + */ public void saveToJsonFile(Object obj, File filepath) { Gson gson = new Gson(); try (FileWriter file = new FileWriter(filepath)) { @@ -82,12 +97,27 @@ public class JsonImpl extends AbstractStorage { } } + /** + * Read JSON content from a file and parse it into an object of the specified class. + * + * @param file The JSON file to read. + * @param classOfT The class of the object to parse the JSON into. + * @param The type of the object. + * @return The parsed object. + */ public T readFromJsonFile(File file, Class classOfT) { Gson gson = new Gson(); String jsonContent = new String(readFileToByteArray(file), StandardCharsets.UTF_8); return gson.fromJson(jsonContent, classOfT); } + /** + * Read the contents of a file and return them as a byte array. + * + * @param file The file to read. + * @return The byte array representing the file's content. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") public byte[] readFileToByteArray(File file) { byte[] fileBytes = new byte[(int) file.length()]; try (FileInputStream fis = new FileInputStream(file)) { @@ -98,6 +128,7 @@ public class JsonImpl extends AbstractStorage { return fileBytes; } + // Retrieve a set of unique user UUIDs based on JSON data files in the 'data' folder. @Override public Set getUniqueUsers(boolean legacy) { // No legacy files diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/YAMLImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/YAMLImpl.java index 4006cfab..4d67a8aa 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/YAMLImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/YAMLImpl.java @@ -31,6 +31,9 @@ import java.io.IOException; import java.util.*; import java.util.concurrent.CompletableFuture; +/** + * A data storage implementation that uses YAML files to store player data, with support for legacy data. + */ public class YAMLImpl extends AbstractStorage implements LegacyDataStorageInterface { @SuppressWarnings("ResultOfMethodCallIgnored") @@ -45,6 +48,12 @@ public class YAMLImpl extends AbstractStorage implements LegacyDataStorageInterf return StorageType.YAML; } + /** + * Get the file associated with a player's UUID for storing YAML data. + * + * @param uuid The UUID of the player. + * @return The file for the player's data. + */ public File getPlayerDataFile(UUID uuid) { return new File(plugin.getDataFolder(), "data" + File.separator + uuid + ".yml"); } @@ -109,7 +118,13 @@ public class YAMLImpl extends AbstractStorage implements LegacyDataStorageInterf return uuids; } - public StatisticData getStatistics(ConfigurationSection section) { + /** + * Parse statistics data from a YAML ConfigurationSection. + * + * @param section The ConfigurationSection containing statistics data. + * @return The parsed StatisticData object. + */ + private StatisticData getStatistics(ConfigurationSection section) { if (section == null) return StatisticData.empty(); else { @@ -123,6 +138,7 @@ public class YAMLImpl extends AbstractStorage implements LegacyDataStorageInterf @Override public CompletableFuture> getLegacyPlayerData(UUID uuid) { + // Retrieve legacy player data (YAML format) for a given UUID. var builder = new PlayerData.Builder().setName(""); File bagFile = new File(plugin.getDataFolder(), "data/fishingbag/" + uuid + ".yml"); if (bagFile.exists()) { diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/user/OfflineUserImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/user/OfflineUserImpl.java index 1cbd96c9..29feec95 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/user/OfflineUserImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/user/OfflineUserImpl.java @@ -36,6 +36,9 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +/** + * Implementation of the OfflineUser interface for representing offline player data. + */ public class OfflineUserImpl implements OfflineUser { private final UUID uuid; @@ -43,13 +46,22 @@ public class OfflineUserImpl implements OfflineUser { private final FishingBagHolder holder; private final EarningData earningData; private final Statistics statistics; - public static OfflineUserImpl LOCKED_USER = new OfflineUserImpl(UUID.randomUUID(), "", PlayerData.empty()); + public static OfflineUserImpl LOCKED_USER = new OfflineUserImpl(UUID.randomUUID(), "-locked-", PlayerData.empty()); + /** + * Constructor to create an OfflineUserImpl instance. + * + * @param uuid The UUID of the player. + * @param name The name of the player. + * @param playerData The player's data, including bag contents, earnings, and statistics. + */ public OfflineUserImpl(UUID uuid, String name, PlayerData playerData) { this.name = name; this.uuid = uuid; this.holder = new FishingBagHolder(uuid); OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid); + + // Set up the inventory for the FishingBagHolder this.holder.setInventory(InventoryUtils.createInventory(this.holder, playerData.getBagData().size, AdventureManagerImpl.getInstance().getComponentFromMiniMessage( PlaceholderManagerImpl.getInstance().parse( @@ -57,6 +69,7 @@ public class OfflineUserImpl implements OfflineUser { ) ))); this.holder.setItems(InventoryUtils.getInventoryItems(playerData.getBagData().serialized)); + this.earningData = playerData.getEarningData(); this.statistics = new Statistics(playerData.getStatistics()); } @@ -94,6 +107,7 @@ public class OfflineUserImpl implements OfflineUser { @Override public PlayerData getPlayerData() { + // Create a new PlayerData instance based on the stored information return new PlayerData.Builder() .setBagData(new InventoryData(InventoryUtils.stacksToBase64(holder.getInventory().getStorageContents()), holder.getInventory().getSize())) .setEarningData(earningData) diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/user/OnlineUserImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/user/OnlineUserImpl.java index f1c72d5e..ea8f63d1 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/user/OnlineUserImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/user/OnlineUserImpl.java @@ -21,10 +21,19 @@ import net.momirealms.customfishing.api.data.PlayerData; import net.momirealms.customfishing.api.data.user.OnlineUser; import org.bukkit.entity.Player; +/** + * Implementation of the OnlineUser interface, extending OfflineUserImpl to represent online player data. + */ public class OnlineUserImpl extends OfflineUserImpl implements OnlineUser { private final Player player; + /** + * Constructor to create an OnlineUserImpl instance. + * + * @param player The online player associated with this user. + * @param playerData The player's data, including bag contents, earnings, and statistics. + */ public OnlineUserImpl(Player player, PlayerData playerData) { super(player.getUniqueId(), player.getName(), playerData); this.player = player; diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/ArmorStandUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/ArmorStandUtils.java index 559ed0bf..a7e7d1a9 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ArmorStandUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/ArmorStandUtils.java @@ -33,14 +33,30 @@ import org.bukkit.inventory.ItemStack; import java.util.*; import java.util.concurrent.TimeUnit; +/** + * Utility class for managing armor stands and sending related packets. + */ public class ArmorStandUtils { + /** + * Creates a destroy packet for removing an armor stand entity. + * + * @param id The ID of the armor stand entity to destroy + * @return The PacketContainer representing the destroy packet + */ public static PacketContainer getDestroyPacket(int id) { PacketContainer destroyPacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY); destroyPacket.getIntLists().write(0, List.of(id)); return destroyPacket; } + /** + * Creates a spawn packet for an armor stand entity at the specified location. + * + * @param id The ID of the armor stand entity to spawn + * @param location The location where the armor stand entity should be spawned + * @return The PacketContainer representing the spawn packet + */ public static PacketContainer getSpawnPacket(int id, Location location) { PacketContainer entityPacket = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY); entityPacket.getModifier().write(0, id); @@ -52,6 +68,12 @@ public class ArmorStandUtils { return entityPacket; } + /** + * Creates a metadata packet for updating the metadata of an armor stand entity. + * + * @param id The ID of the armor stand entity + * @return The PacketContainer representing the metadata packet + */ public static PacketContainer getMetaPacket(int id) { PacketContainer metaPacket = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); metaPacket.getIntegers().write(0, id); @@ -64,6 +86,13 @@ public class ArmorStandUtils { return metaPacket; } + /** + * Sets the value list in a PacketContainer's DataWatcher from a WrappedDataWatcher. + * + * @param metaPacket The PacketContainer representing the metadata packet + * @param wrappedDataWatcher The WrappedDataWatcher containing the value list + */ + @SuppressWarnings("DuplicatedCode") private static void setValueList(PacketContainer metaPacket, WrappedDataWatcher wrappedDataWatcher) { List wrappedDataValueList = Lists.newArrayList(); wrappedDataWatcher.getWatchableObjects().stream().filter(Objects::nonNull).forEach(entry -> { @@ -73,6 +102,13 @@ public class ArmorStandUtils { metaPacket.getDataValueCollectionModifier().write(0, wrappedDataValueList); } + /** + * Creates a metadata packet for updating the metadata of an armor stand entity with a custom Component. + * + * @param id The ID of the armor stand entity + * @param component The Component to set as metadata + * @return The PacketContainer representing the metadata packet + */ public static PacketContainer getMetaPacket(int id, Component component) { PacketContainer metaPacket = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); metaPacket.getIntegers().write(0, id); @@ -85,6 +121,11 @@ public class ArmorStandUtils { return metaPacket; } + /** + * Creates a DataWatcher for an invisible armor stand entity. + * + * @return The created DataWatcher + */ public static WrappedDataWatcher createDataWatcher() { WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher(); WrappedDataWatcher.Serializer serializer1 = WrappedDataWatcher.Registry.get(Boolean.class); @@ -95,6 +136,12 @@ public class ArmorStandUtils { return wrappedDataWatcher; } + /** + * Creates a DataWatcher for an invisible armor stand entity with a custom Component. + * + * @param component The Component to set in the DataWatcher + * @return The created DataWatcher + */ public static WrappedDataWatcher createDataWatcher(Component component) { WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher(); WrappedDataWatcher.Serializer serializer1 = WrappedDataWatcher.Registry.get(Boolean.class); @@ -107,6 +154,13 @@ public class ArmorStandUtils { return wrappedDataWatcher; } + /** + * Creates an equipment packet for equipping an armor stand with an ItemStack. + * + * @param id The ID of the armor stand entity + * @param itemStack The ItemStack to equip + * @return The PacketContainer representing the equipment packet + */ public static PacketContainer getEquipPacket(int id, ItemStack itemStack) { PacketContainer equipPacket = new PacketContainer(PacketType.Play.Server.ENTITY_EQUIPMENT); equipPacket.getIntegers().write(0, id); @@ -116,18 +170,34 @@ public class ArmorStandUtils { return equipPacket; } - public static void sendFakeItem(Player player, Location location, ItemStack itemStack, int time) { + /** + * Sends a fake armor stand entity with item on head to a player at the specified location. + * + * @param player The player to send the entity to + * @param location The location where the entity should appear + * @param itemStack The ItemStack to represent the entity + * @param seconds The duration (in seconds) the entity should be displayed + */ + public static void sendFakeItem(Player player, Location location, ItemStack itemStack, int seconds) { int id = new Random().nextInt(Integer.MAX_VALUE); CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getSpawnPacket(id, location.clone().subtract(0,1,0))); CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getMetaPacket(id)); CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getEquipPacket(id, itemStack)); - CustomFishingPlugin.get().getScheduler().runTaskAsyncLater(() -> CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getDestroyPacket(id)), time * 50L, TimeUnit.MILLISECONDS); + CustomFishingPlugin.get().getScheduler().runTaskAsyncLater(() -> CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getDestroyPacket(id)), seconds * 50L, TimeUnit.MILLISECONDS); } - public static void sendHologram(Player player, Location location, Component component, int time) { + /** + * Sends a hologram (armor stand with custom text) to a player at the specified location. + * + * @param player The player to send the hologram to + * @param location The location where the hologram should appear + * @param component The Component representing the hologram's text + * @param seconds The duration (in seconds) the hologram should be displayed + */ + public static void sendHologram(Player player, Location location, Component component, int seconds) { int id = new Random().nextInt(Integer.MAX_VALUE); CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getSpawnPacket(id, location.clone().subtract(0,1,0))); CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getMetaPacket(id, component)); - CustomFishingPlugin.get().getScheduler().runTaskAsyncLater(() -> CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getDestroyPacket(id)), time * 50L, TimeUnit.MILLISECONDS); + CustomFishingPlugin.get().getScheduler().runTaskAsyncLater(() -> CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getDestroyPacket(id)), seconds * 50L, TimeUnit.MILLISECONDS); } } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java index 51cfbc15..2b7de001 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java @@ -31,6 +31,16 @@ import java.util.jar.JarInputStream; public class ClassUtils { + /** + * Attempts to find a class within a JAR file that extends or implements a given class or interface. + * + * @param file The JAR file in which to search for the class. + * @param clazz The base class or interface to match against. + * @param The type of the base class or interface. + * @return A Class object representing the found class, or null if not found. + * @throws IOException If there is an issue reading the JAR file. + * @throws ClassNotFoundException If the specified class cannot be found. + */ @Nullable public static Class findClass( @NotNull File file, diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/CompletableFutures.java b/plugin/src/main/java/net/momirealms/customfishing/util/CompletableFutures.java index 96223153..7e9dc660 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/CompletableFutures.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/CompletableFutures.java @@ -24,9 +24,17 @@ import java.util.concurrent.CompletableFuture; import java.util.stream.Collector; import java.util.stream.Stream; -public final class CompletableFutures { +public class CompletableFutures { + private CompletableFutures() {} + /** + * A collector for collecting a stream of CompletableFuture instances into a single CompletableFuture that completes + * when all of the input CompletableFutures complete. + * + * @param The type of CompletableFuture. + * @return A collector for CompletableFuture instances. + */ public static > Collector, CompletableFuture> collector() { return Collector.of( ImmutableList.Builder::new, @@ -36,11 +44,25 @@ public final class CompletableFutures { ); } + /** + * Combines multiple CompletableFuture instances into a single CompletableFuture that completes when all of the input + * CompletableFutures complete. + * + * @param futures A stream of CompletableFuture instances. + * @return A CompletableFuture that completes when all input CompletableFutures complete. + */ public static CompletableFuture allOf(Stream> futures) { CompletableFuture[] arr = futures.toArray(CompletableFuture[]::new); return CompletableFuture.allOf(arr); } + /** + * Combines multiple CompletableFuture instances into a single CompletableFuture that completes when all of the input + * CompletableFutures complete. + * + * @param futures A collection of CompletableFuture instances. + * @return A CompletableFuture that completes when all input CompletableFutures complete. + */ public static CompletableFuture allOf(Collection> futures) { CompletableFuture[] arr = futures.toArray(new CompletableFuture[0]); return CompletableFuture.allOf(arr); diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/ConfigUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/ConfigUtils.java index 9cde9f0d..a0bd9934 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ConfigUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/ConfigUtils.java @@ -31,8 +31,17 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +/** + * Utility class for configuration-related operations. + */ public class ConfigUtils { + /** + * Converts an object into an ArrayList of strings. + * + * @param object The input object + * @return An ArrayList of strings + */ @SuppressWarnings("unchecked") public static ArrayList stringListArgs(Object object) { ArrayList list = new ArrayList<>(); @@ -46,11 +55,23 @@ public class ConfigUtils { return list; } + /** + * Splits a string into a pair of integers using the "~" delimiter. + * + * @param value The input string + * @return A Pair of integers + */ public static Pair splitStringIntegerArgs(String value) { String[] split = value.split("~"); return Pair.of(Integer.parseInt(split[0]), Integer.parseInt(split[1])); } + /** + * Converts a list of strings in the format "key:value" into a list of Pairs with keys and doubles. + * + * @param list The input list of strings + * @return A list of Pairs containing keys and doubles + */ public static List> getWeights(List list) { List> result = new ArrayList<>(list.size()); for (String member : list) { @@ -61,6 +82,12 @@ public class ConfigUtils { return result; } + /** + * Converts an object into a double value. + * + * @param arg The input object + * @return A double value + */ public static double getDoubleValue(Object arg) { if (arg instanceof Double d) { return d; @@ -70,6 +97,12 @@ public class ConfigUtils { return 0; } + /** + * Converts a list of strings in the format "key:value" into a list of Pairs with keys and WeightModifiers. + * + * @param modList The input list of strings + * @return A list of Pairs containing keys and WeightModifiers + */ public static List> getModifiers(List modList) { List> result = new ArrayList<>(modList.size()); for (String member : modList) { @@ -81,9 +114,10 @@ public class ConfigUtils { } /** - * Create a data file if not exists - * @param file file path - * @return yaml data + * Reads data from a YAML configuration file and creates it if it doesn't exist. + * + * @param file The file path + * @return The YamlConfiguration */ @SuppressWarnings("ResultOfMethodCallIgnored") public static YamlConfiguration readData(File file) { @@ -99,6 +133,13 @@ public class ConfigUtils { return YamlConfiguration.loadConfiguration(file); } + /** + * Parses a WeightModifier from a string representation. + * + * @param text The input string + * @return A WeightModifier based on the provided text + * @throws IllegalArgumentException if the weight format is invalid + */ public static WeightModifier getModifier(String text) { if (text.length() == 0) { throw new IllegalArgumentException("Weight format is invalid."); @@ -130,7 +171,7 @@ public class ConfigUtils { return (player, weight) -> { String temp = formula; if (hasPapi) - temp = PlaceholderManagerImpl.getInstance().parseCacheable(player, formula); + temp = PlaceholderManagerImpl.getInstance().parseCacheablePlaceholders(player, formula); Expression expression = new ExpressionBuilder(temp) .variables("0") .build() diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/DynamicText.java b/plugin/src/main/java/net/momirealms/customfishing/util/DynamicText.java index af0ebf8d..42dbef4d 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/DynamicText.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/DynamicText.java @@ -30,7 +30,6 @@ public class DynamicText { private String originalValue; private String latestValue; private String[] placeholders; - private String[] previousParsedValues; public DynamicText(Player owner, String rawValue) { this.owner = owner; @@ -38,6 +37,8 @@ public class DynamicText { } private void analyze(String value) { + // Analyze the provided text to find and replace placeholders with '%s'. + // Store the original value, placeholders, and the initial latest value. List placeholdersOwner = new ArrayList<>(PlaceholderManagerImpl.getInstance().detectPlaceholders(value)); String origin = value; for (String placeholder : placeholdersOwner) { @@ -53,6 +54,7 @@ public class DynamicText { } public boolean update(Map placeholders) { + // Update the dynamic text by replacing placeholders with actual values. String string = originalValue; if (this.placeholders.length != 0) { PlaceholderManagerImpl placeholderManagerImpl = PlaceholderManagerImpl.getInstance(); @@ -67,6 +69,7 @@ public class DynamicText { } } if (!latestValue.equals(string)) { + // If the updated value is different from the latest value, update it. latestValue = string; return true; } diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/FakeItemUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/FakeItemUtils.java index 9ec3a617..9818ee87 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/FakeItemUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/FakeItemUtils.java @@ -32,14 +32,30 @@ import java.util.List; import java.util.Objects; import java.util.UUID; +/** + * Utility class for managing fake item entities using PacketContainers. + */ public class FakeItemUtils { + /** + * Creates a destroy packet for removing a fake item entity. + * + * @param id The ID of the fake item entity to destroy + * @return The PacketContainer representing the destroy packet + */ public static PacketContainer getDestroyPacket(int id) { PacketContainer destroyPacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY); destroyPacket.getIntLists().write(0, List.of(id)); return destroyPacket; } + /** + * Creates a spawn packet for a fake item entity at the specified location. + * + * @param id The ID of the fake item entity to spawn + * @param location The location where the fake item entity should be spawned + * @return The PacketContainer representing the spawn packet + */ public static PacketContainer getSpawnPacket(int id, Location location) { PacketContainer entityPacket = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY); entityPacket.getModifier().write(0, id); @@ -51,6 +67,13 @@ public class FakeItemUtils { return entityPacket; } + /** + * Creates a metadata packet for updating the metadata of a fake item entity. + * + * @param id The ID of the fake item entity + * @param itemStack The ItemStack to update the metadata with + * @return The PacketContainer representing the metadata packet + */ public static PacketContainer getMetaPacket(int id, ItemStack itemStack) { PacketContainer metaPacket = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); metaPacket.getIntegers().write(0, id); @@ -63,6 +86,13 @@ public class FakeItemUtils { return metaPacket; } + /** + * Creates a teleport packet for moving a fake item entity to the specified location. + * + * @param id The ID of the fake item entity to teleport + * @param location The location to teleport the fake item entity to + * @return The PacketContainer representing the teleport packet + */ public static PacketContainer getTpPacket(int id, Location location) { PacketContainer tpPacket = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT); tpPacket.getModifier().write(0, id); @@ -72,7 +102,14 @@ public class FakeItemUtils { return tpPacket; } - public static PacketContainer getVelocity(int id, Vector vector) { + /** + * Creates a velocity packet for applying velocity to a fake item entity. + * + * @param id The ID of the fake item entity + * @param vector The velocity vector to apply + * @return The PacketContainer representing the velocity packet + */ + public static PacketContainer getVelocityPacket(int id, Vector vector) { PacketContainer entityPacket = new PacketContainer(PacketType.Play.Server.ENTITY_VELOCITY); entityPacket.getModifier().write(0, id); entityPacket.getIntegers().write(1, (int) (vector.getX() * 8000)); @@ -81,6 +118,12 @@ public class FakeItemUtils { return entityPacket; } + /** + * Creates a DataWatcher for a given ItemStack. + * + * @param itemStack The ItemStack to create the DataWatcher for + * @return The created DataWatcher + */ public static WrappedDataWatcher createDataWatcher(ItemStack itemStack) { WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher(); wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(8, WrappedDataWatcher.Registry.getItemStackSerializer(false)), itemStack); @@ -88,6 +131,13 @@ public class FakeItemUtils { return wrappedDataWatcher; } + /** + * Sets the value list in a PacketContainer's DataWatcher from a WrappedDataWatcher. + * + * @param metaPacket The PacketContainer representing the metadata packet + * @param wrappedDataWatcher The WrappedDataWatcher containing the value list + */ + @SuppressWarnings("DuplicatedCode") private static void setValueList(PacketContainer metaPacket, WrappedDataWatcher wrappedDataWatcher) { List wrappedDataValueList = Lists.newArrayList(); wrappedDataWatcher.getWatchableObjects().stream().filter(Objects::nonNull).forEach(entry -> { diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/ItemUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/ItemUtils.java index 7bf42a6f..b1603ef2 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ItemUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/ItemUtils.java @@ -36,8 +36,17 @@ import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.ItemMeta; +/** + * Utility class for various item-related operations. + */ public class ItemUtils { + /** + * Updates the lore of an NBTItem based on its custom NBT tags. + * + * @param nbtItem The NBTItem to update + * @return The updated NBTItem + */ public static NBTItem updateNBTItemLore(NBTItem nbtItem) { NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); if (cfCompound == null) @@ -84,6 +93,11 @@ public class ItemUtils { return nbtItem; } + /** + * Updates the lore of an ItemStack based on its custom NBT tags. + * + * @param itemStack The ItemStack to update + */ public static void updateItemLore(ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) return; @@ -91,26 +105,104 @@ public class ItemUtils { itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); } - public static void reduceHookDurability(ItemStack itemStack, boolean updateLore) { - if (itemStack == null || itemStack.getType() == Material.AIR) + /** + * Reduces the durability of a fishing hook item. + * + * @param rod The fishing rod ItemStack + * @param updateLore Whether to update the lore after reducing durability + */ + public static void decreaseHookDurability(ItemStack rod, int amount, boolean updateLore) { + if (rod == null || rod.getType() != Material.FISHING_ROD) return; - NBTItem nbtItem = new NBTItem(itemStack); + NBTItem nbtItem = new NBTItem(rod); NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); if (cfCompound != null && cfCompound.hasTag("hook_dur")) { int hookDur = cfCompound.getInteger("hook_dur"); - if (hookDur > 0) { - cfCompound.setInteger("hook_dur", hookDur - 1); - } else if (hookDur != -1) { - cfCompound.removeKey("hook_id"); - cfCompound.removeKey("hook_dur"); - cfCompound.removeKey("hook_id"); + if (hookDur != -1) { + hookDur = Math.max(0, hookDur - amount); + if (hookDur > 0) { + cfCompound.setInteger("hook_dur", hookDur); + } else { + cfCompound.removeKey("hook_id"); + cfCompound.removeKey("hook_dur"); + cfCompound.removeKey("hook_item"); + } } } if (updateLore) updateNBTItemLore(nbtItem); - itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); + rod.setItemMeta(nbtItem.getItem().getItemMeta()); } - public static void loseDurability(ItemStack itemStack, int amount, boolean updateLore) { + /** + * Increases the durability of a fishing hook by a specified amount and optionally updates its lore. + * + * @param rod The fishing rod ItemStack to modify. + * @param amount The amount by which to increase the durability. + * @param updateLore Whether to update the lore of the fishing rod. + */ + public static void increaseHookDurability(ItemStack rod, int amount, boolean updateLore) { + if (rod == null || rod.getType() != Material.FISHING_ROD) + return; + NBTItem nbtItem = new NBTItem(rod); + NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); + if (cfCompound != null && cfCompound.hasTag("hook_dur")) { + int hookDur = cfCompound.getInteger("hook_dur"); + if (hookDur != -1) { + String id = cfCompound.getString("hook_id"); + HookSetting setting = CustomFishingPlugin.get().getHookManager().getHookSetting(id); + if (setting == null) { + cfCompound.removeKey("hook_id"); + cfCompound.removeKey("hook_dur"); + cfCompound.removeKey("hook_item"); + } else { + hookDur = Math.min(setting.getMaxDurability(), hookDur + amount); + cfCompound.setInteger("hook_dur", hookDur); + } + } + } + if (updateLore) updateNBTItemLore(nbtItem); + rod.setItemMeta(nbtItem.getItem().getItemMeta()); + } + + /** + * Sets the durability of a fishing hook to a specific amount and optionally updates its lore. + * + * @param rod The fishing rod ItemStack to modify. + * @param amount The new durability value to set. + * @param updateLore Whether to update the lore of the fishing rod. + */ + public static void setHookDurability(ItemStack rod, int amount, boolean updateLore) { + if (rod == null || rod.getType() != Material.FISHING_ROD) + return; + NBTItem nbtItem = new NBTItem(rod); + NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); + if (cfCompound != null && cfCompound.hasTag("hook_dur")) { + int hookDur = cfCompound.getInteger("hook_dur"); + if (hookDur != -1) { + String id = cfCompound.getString("hook_id"); + HookSetting setting = CustomFishingPlugin.get().getHookManager().getHookSetting(id); + if (setting == null) { + cfCompound.removeKey("hook_id"); + cfCompound.removeKey("hook_dur"); + cfCompound.removeKey("hook_item"); + } else { + hookDur = Math.min(setting.getMaxDurability(), amount); + cfCompound.setInteger("hook_dur", hookDur); + } + } + } + if (updateLore) updateNBTItemLore(nbtItem); + rod.setItemMeta(nbtItem.getItem().getItemMeta()); + } + + /** + * Decreases the durability of an item and updates its lore. + * + * @param itemStack The ItemStack to reduce durability for + * @param amount The amount by which to reduce durability + * @param updateLore Whether to update the lore after reducing durability + */ + public static void decreaseDurability(ItemStack itemStack, int amount, boolean updateLore) { if (itemStack == null || itemStack.getType() == Material.AIR) return; int unBreakingLevel = itemStack.getEnchantmentLevel(Enchantment.DURABILITY); @@ -145,7 +237,14 @@ public class ItemUtils { } } - public static void addDurability(ItemStack itemStack, int amount, boolean updateLore) { + /** + * Increases the durability of an item and updates its lore. + * + * @param itemStack The ItemStack to increase durability for + * @param amount The amount by which to increase durability + * @param updateLore Whether to update the lore after increasing durability + */ + public static void increaseDurability(ItemStack itemStack, int amount, boolean updateLore) { if (itemStack == null || itemStack.getType() == Material.AIR) return; NBTItem nbtItem = new NBTItem(itemStack); @@ -167,6 +266,13 @@ public class ItemUtils { itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); } + /** + * Sets the durability of an item and updates its lore. + * + * @param itemStack The ItemStack to set durability for + * @param amount The new durability value + * @param updateLore Whether to update the lore after setting durability + */ public static void setDurability(ItemStack itemStack, int amount, boolean updateLore) { if (itemStack == null || itemStack.getType() == Material.AIR) return; @@ -192,6 +298,12 @@ public class ItemUtils { itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); } + /** + * Retrieves the current durability of an item. + * + * @param itemStack The ItemStack to get durability from + * @return The current durability value + */ public static int getDurability(ItemStack itemStack) { if (!(itemStack.getItemMeta() instanceof Damageable damageable)) return -1; @@ -206,6 +318,14 @@ public class ItemUtils { } } + /** + * Gives a certain amount of an item to a player, handling stacking and item drops. + * + * @param player The player to give the item to + * @param itemStack The ItemStack to give + * @param amount The amount of items to give + * @return The actual amount of items given + */ public static int giveCertainAmountOfItem(Player player, ItemStack itemStack, int amount) { PlayerInventory inventory = player.getInventory(); ItemMeta meta = itemStack.getItemMeta(); diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/LocationUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/LocationUtils.java index 2ee76af0..b3098602 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/LocationUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/LocationUtils.java @@ -21,6 +21,13 @@ import org.bukkit.Location; public class LocationUtils { + /** + * Calculates the Euclidean distance between two locations in 3D space. + * + * @param location1 The first location + * @param location2 The second location + * @return The Euclidean distance between the two locations + */ public static double getDistance(Location location1, Location location2) { return Math.sqrt(Math.pow(location2.getX() - location1.getX(), 2) + Math.pow(location2.getY() - location1.getY(), 2) + diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/NBTUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/NBTUtils.java index f1c8b1c9..d8f73d97 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/NBTUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/NBTUtils.java @@ -24,8 +24,14 @@ import org.bukkit.configuration.MemorySection; import java.util.*; +/** + * Utility class for working with NBT (Named Binary Tag) data. + */ public class NBTUtils { + /** + * Inner class representing a stack element used during NBT data conversion. + */ public static class StackElement { final Map currentMap; final NBTCompound currentNbtCompound; @@ -36,6 +42,12 @@ public class NBTUtils { } } + /** + * Converts data from a Bukkit YAML configuration to NBT tags. + * + * @param nbtCompound The target NBT compound + * @param map The source map from Bukkit YAML + */ @SuppressWarnings("unchecked") public static void setTagsFromBukkitYAML(NBTCompound nbtCompound, Map map) { @@ -69,6 +81,7 @@ public class NBTUtils { } } + // Private helper method private static void setListValue(String key, String value, NBTCompound nbtCompound) { String[] parts = getTypeAndData(value); String type = parts[0]; @@ -89,6 +102,7 @@ public class NBTUtils { } } + // Private helper method private static void setSingleValue(String key, String value, NBTCompound nbtCompound) { String[] parts = getTypeAndData(value); String type = parts[0]; @@ -120,6 +134,12 @@ public class NBTUtils { } } + /** + * Converts an NBT compound to a map of key-value pairs. + * + * @param nbtCompound The source NBT compound + * @return A map representing the NBT data + */ public static Map compoundToMap(ReadWriteNBT nbtCompound){ Map map = new HashMap<>(); for (String key : nbtCompound.getKeys()) { @@ -155,6 +175,12 @@ public class NBTUtils { return map; } + /** + * Splits a value into type and data components. + * + * @param str The input value string + * @return An array containing type and data strings + */ private static String[] getTypeAndData(String str) { String[] parts = str.split("\\s+", 2); if (parts.length != 2) { @@ -165,6 +191,12 @@ public class NBTUtils { return new String[]{type, data}; } + /** + * Splits a value containing arrays into individual elements. + * + * @param value The input value containing arrays + * @return An array of individual elements + */ private static String[] splitValue(String value) { return value.substring(value.indexOf('[') + 1, value.lastIndexOf(']')) .replaceAll("\\s", "") diff --git a/plugin/src/main/java/net/momirealms/customfishing/version/VersionManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/version/VersionManagerImpl.java index 70e98427..fcef8e73 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/version/VersionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/version/VersionManagerImpl.java @@ -28,6 +28,9 @@ import java.net.URL; import java.net.URLConnection; import java.util.concurrent.CompletableFuture; +/** + * This class implements the VersionManager interface and is responsible for managing version-related information. + */ public class VersionManagerImpl implements VersionManager { private final boolean isNewerThan1_19_R2; @@ -41,9 +44,13 @@ public class VersionManagerImpl implements VersionManager { @SuppressWarnings("deprecation") public VersionManagerImpl(CustomFishingPluginImpl plugin) { this.plugin = plugin; + + // Get the server version serverVersion = plugin.getServer().getClass().getPackage().getName().split("\\.")[3]; String[] split = serverVersion.split("_"); int main_ver = Integer.parseInt(split[1]); + + // Determine if the server version is newer than 1_19_R2 and 1_20_R1 if (main_ver >= 20) { isNewerThan1_19_R2 = true; isNewerThan1_20 = true; @@ -54,12 +61,20 @@ public class VersionManagerImpl implements VersionManager { isNewerThan1_19_R2 = false; isNewerThan1_20 = false; } + + // Check if the server is Spigot String server_name = plugin.getServer().getName(); this.isSpigot = server_name.equals("CraftBukkit"); + + // Check if the server is Folia try { Class.forName("io.papermc.paper.threadedregions.scheduler.AsyncScheduler"); this.isFolia = true; - } catch (ClassNotFoundException ignored) {} + } catch (ClassNotFoundException ignored) { + + } + + // Get the plugin version this.pluginVersion = plugin.getDescription().getVersion(); } @@ -93,6 +108,7 @@ public class VersionManagerImpl implements VersionManager { return serverVersion; } + // Method to asynchronously check for plugin updates @Override public CompletableFuture checkUpdate() { CompletableFuture updateFuture = new CompletableFuture<>(); @@ -119,6 +135,7 @@ public class VersionManagerImpl implements VersionManager { return updateFuture; } + // Method to compare two version strings private boolean compareVer(String newV, String currentV) { if (newV == null || currentV == null || newV.isEmpty() || currentV.isEmpty()) { return false; diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 2b34a65d..349fefc7 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -136,11 +136,11 @@ other-settings: thread-pool-settings: # The size of the core Thread pool, that is, the size of the Thread pool when there is no task to execute # Increase the size of corePoolSize when you are running a large server with many players fishing at the same time - corePoolSize: 4 + corePoolSize: 8 # The maximum number of threads allowed to be created in the Thread pool. The current number of threads in the Thread pool will not exceed this value - maximumPoolSize: 8 + maximumPoolSize: 16 # If a thread is idle for more than this attribute value, it will exit due to timeout - keepAliveTime: 10 + keepAliveTime: 30 # Event priority: MONITOR HIGHEST HIGH NORMAL LOW LOWEST event-priority: NORMAL diff --git a/plugin/src/main/resources/contents/totem/default.yml b/plugin/src/main/resources/contents/totem/default.yml index 79f01737..5818bfbd 100644 --- a/plugin/src/main/resources/contents/totem/default.yml +++ b/plugin/src/main/resources/contents/totem/default.yml @@ -7,11 +7,11 @@ double_loot_totem: 4: - '*_STAIRS{face=east;half=top} OBSERVER{face=south} *_STAIRS{face=west;half=top}' 3: - - 'AIR CRYING_OBSIDIAN AIR' + - 'AIR CRYING_OBSIDIAN AIR' 2: - - 'AIR *_LOG{axis=y}||*_PILLAR{axis=y} AIR' + - 'AIR *_LOG{axis=y}||*_PILLAR{axis=y} AIR' 1: - - 'AIR ANVIL AIR' + - 'AIR||GRASS||SNOW ANVIL AIR||GRASS||SNOW' effects: double_loot: type: multiple-loot