diff --git a/README.md b/README.md index 628c8ebf..848b560e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Gitbook -CustomFishing is a Paper plugin that provides minigames and a powerful condition & action library for fishing. +CustomFishing is a Paper plugin that provides minigames and a powerful condition & action system for fishing. With the new concept of weight system, CustomFishing brings unlimited customization possibilities and best performance. ## How to build diff --git a/api/build.gradle.kts b/api/build.gradle.kts index c4df5e65..7c3b1d7b 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -1,11 +1,48 @@ +plugins { + id("io.github.goooler.shadow") version "8.1.7" +} + +repositories { + maven("https://jitpack.io/") // rtag + maven("https://papermc.io/repo/repository/maven-public/") + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // papi +} + dependencies { - compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") - compileOnly("com.comphenix.protocol:ProtocolLib:5.1.0") - compileOnly("de.tr7zw:item-nbt-api:2.12.4") + implementation(project(":common")) + implementation("dev.dejvokep:boosted-yaml:${rootProject.properties["boosted_yaml_version"]}") + implementation("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}") { + exclude(module = "adventure-bom") + exclude(module = "checker-qual") + exclude(module = "annotations") + } + implementation("com.saicone.rtag:rtag:${rootProject.properties["rtag_version"]}") + implementation("com.saicone.rtag:rtag-item:${rootProject.properties["rtag_version"]}") + compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT") + compileOnly("com.google.code.gson:gson:${rootProject.properties["gson_version"]}") + compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}") + compileOnly("com.github.Xiao-MoMi:Sparrow-Heart:${rootProject.properties["sparrow_heart_version"]}") +// compileOnly(files("libs/Sparrow-Heart-${rootProject.properties["sparrow_heart_version"]}.jar")) +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) } tasks { shadowJar { - relocate ("de.tr7zw.changeme", "net.momirealms.customfishing.libraries") + relocate("net.kyori", "net.momirealms.customfishing.libraries") + relocate("dev.dejvokep", "net.momirealms.customfishing.libraries") + relocate ("com.saicone.rtag", "net.momirealms.customfishing.libraries.rtag") } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/BukkitCustomFishingPlugin.java b/api/src/main/java/net/momirealms/customfishing/api/BukkitCustomFishingPlugin.java new file mode 100644 index 00000000..72d3e230 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/BukkitCustomFishingPlugin.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api; + +import net.momirealms.customfishing.api.integration.IntegrationManager; +import net.momirealms.customfishing.api.mechanic.action.ActionManager; +import net.momirealms.customfishing.api.mechanic.bag.BagManager; +import net.momirealms.customfishing.api.mechanic.block.BlockManager; +import net.momirealms.customfishing.api.mechanic.competition.CompetitionManager; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.effect.EffectManager; +import net.momirealms.customfishing.api.mechanic.entity.EntityManager; +import net.momirealms.customfishing.api.mechanic.event.EventManager; +import net.momirealms.customfishing.api.mechanic.fishing.FishingManager; +import net.momirealms.customfishing.api.mechanic.game.GameManager; +import net.momirealms.customfishing.api.mechanic.hook.HookManager; +import net.momirealms.customfishing.api.mechanic.item.ItemManager; +import net.momirealms.customfishing.api.mechanic.loot.LootManager; +import net.momirealms.customfishing.api.mechanic.market.MarketManager; +import net.momirealms.customfishing.api.mechanic.misc.cooldown.CoolDownManager; +import net.momirealms.customfishing.api.mechanic.misc.placeholder.PlaceholderManager; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager; +import net.momirealms.customfishing.api.mechanic.statistic.StatisticsManager; +import net.momirealms.customfishing.api.mechanic.totem.TotemManager; +import net.momirealms.customfishing.api.storage.StorageManager; +import net.momirealms.customfishing.common.dependency.DependencyManager; +import net.momirealms.customfishing.common.locale.TranslationManager; +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import net.momirealms.customfishing.common.plugin.scheduler.AbstractJavaScheduler; +import net.momirealms.customfishing.common.sender.SenderFactory; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import java.io.File; + +public abstract class BukkitCustomFishingPlugin implements CustomFishingPlugin, Reloadable { + + private static BukkitCustomFishingPlugin instance; + private final Plugin boostrap; + + protected EventManager eventManager; + protected ConfigManager configManager; + protected RequirementManager requirementManager; + protected ActionManager actionManager; + protected SenderFactory senderFactory; + protected PlaceholderManager placeholderManager; + protected AbstractJavaScheduler scheduler; + protected ItemManager itemManager; + protected IntegrationManager integrationManager; + protected CompetitionManager competitionManager; + protected MarketManager marketManager; + protected StorageManager storageManager; + protected LootManager lootManager; + protected CoolDownManager coolDownManager; + protected EntityManager entityManager; + protected BlockManager blockManager; + protected StatisticsManager statisticsManager; + protected EffectManager effectManager; + protected HookManager hookManager; + protected BagManager bagManager; + protected DependencyManager dependencyManager; + protected TranslationManager translationManager; + protected TotemManager totemManager; + protected FishingManager fishingManager; + protected GameManager gameManager; + + public BukkitCustomFishingPlugin(Plugin boostrap) { + if (!boostrap.getName().equals("CustomFishing")) { + throw new IllegalArgumentException("CustomFishing plugin requires custom fishing plugin"); + } + this.boostrap = boostrap; + instance = this; + } + + public static BukkitCustomFishingPlugin getInstance() { + if (instance == null) { + throw new IllegalArgumentException("Plugin not initialized"); + } + return instance; + } + + public EventManager getEventManager() { + return eventManager; + } + + @Override + public ConfigManager getConfigManager() { + return configManager; + } + + public RequirementManager getRequirementManager() { + return requirementManager; + } + + public ActionManager getActionManager() { + return actionManager; + } + + public SenderFactory getSenderFactory() { + return senderFactory; + } + + public File getDataFolder() { + return boostrap.getDataFolder(); + } + + public PlaceholderManager getPlaceholderManager() { + return placeholderManager; + } + + public ItemManager getItemManager() { + return itemManager; + } + + public IntegrationManager getIntegrationManager() { + return integrationManager; + } + + @Override + public AbstractJavaScheduler getScheduler() { + return scheduler; + } + + public CompetitionManager getCompetitionManager() { + return competitionManager; + } + + public MarketManager getMarketManager() { + return marketManager; + } + + public StorageManager getStorageManager() { + return storageManager; + } + + public LootManager getLootManager() { + return lootManager; + } + + public EntityManager getEntityManager() { + return entityManager; + } + + public HookManager getHookManager() { + return hookManager; + } + + public BlockManager getBlockManager() { + return blockManager; + } + + public CoolDownManager getCoolDownManager() { + return coolDownManager; + } + + public StatisticsManager getStatisticsManager() { + return statisticsManager; + } + + public EffectManager getEffectManager() { + return effectManager; + } + + public BagManager getBagManager() { + return bagManager; + } + + public TotemManager getTotemManager() { + return totemManager; + } + + public FishingManager getFishingManager() { + return fishingManager; + } + + public GameManager getGameManager() { + return gameManager; + } + + public Plugin getBoostrap() { + return boostrap; + } + + @Override + public DependencyManager getDependencyManager() { + return dependencyManager; + } + + @Override + public TranslationManager getTranslationManager() { + return translationManager; + } + + public abstract void enable(); + + public abstract void debug(Object message); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/CustomFishingPlugin.java b/api/src/main/java/net/momirealms/customfishing/api/CustomFishingPlugin.java deleted file mode 100644 index 649c0612..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/CustomFishingPlugin.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api; - -import net.momirealms.customfishing.api.manager.*; -import net.momirealms.customfishing.api.scheduler.Scheduler; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.java.JavaPlugin; -import org.jetbrains.annotations.NotNull; - -public abstract class CustomFishingPlugin extends JavaPlugin { - - protected boolean initialized; - protected Scheduler scheduler; - protected CommandManager commandManager; - protected VersionManager versionManager; - protected ItemManager itemManager; - protected RequirementManager requirementManager; - protected ActionManager actionManager; - protected LootManager lootManager; - protected FishingManager fishingManager; - protected EffectManager effectManager; - protected EntityManager entityManager; - protected BlockManager blockManager; - protected AdventureManager adventure; - protected BagManager bagManager; - protected GameManager gameManager; - protected MarketManager marketManager; - protected IntegrationManager integrationManager; - protected CompetitionManager competitionManager; - protected StorageManager storageManager; - protected PlaceholderManager placeholderManager; - protected StatisticsManager statisticsManager; - protected TotemManager totemManager; - protected HookManager hookManager; - - private static CustomFishingPlugin instance; - - public CustomFishingPlugin() { - instance = this; - } - - public static CustomFishingPlugin get() { - return getInstance(); - } - - @NotNull - public static CustomFishingPlugin getInstance() { - return instance; - } - - public Scheduler getScheduler() { - return scheduler; - } - - public CommandManager getCommandManager() { - return commandManager; - } - - public VersionManager getVersionManager() { - return versionManager; - } - - public RequirementManager getRequirementManager() { - return requirementManager; - } - - public ActionManager getActionManager() { - return actionManager; - } - - public GameManager getGameManager() { - return gameManager; - } - - public BlockManager getBlockManager() { - return blockManager; - } - - public EntityManager getEntityManager() { - return entityManager; - } - - public ItemManager getItemManager() { - return itemManager; - } - - public EffectManager getEffectManager() { - return effectManager; - } - - public MarketManager getMarketManager() { - return marketManager; - } - - public FishingManager getFishingManager() { - return fishingManager; - } - - public AdventureManager getAdventure() { - return adventure; - } - - public BagManager getBagManager() { - return bagManager; - } - - public LootManager getLootManager() { - return lootManager; - } - - public StorageManager getStorageManager() { - return storageManager; - } - - public TotemManager getTotemManager() { - return totemManager; - } - - public HookManager getHookManager() { - return hookManager; - } - - public IntegrationManager getIntegrationManager() { - return integrationManager; - } - - public StatisticsManager getStatisticsManager() { - return statisticsManager; - } - - public PlaceholderManager getPlaceholderManager() { - return placeholderManager; - } - - public CompetitionManager getCompetitionManager() { - return competitionManager; - } - - public abstract void reload(); - - public abstract YamlConfiguration getConfig(String file); - - public abstract boolean isHookedPluginEnabled(String plugin); - - public abstract void debug(String message); -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/common/Tuple.java b/api/src/main/java/net/momirealms/customfishing/api/common/Tuple.java deleted file mode 100644 index 2d50e10d..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/common/Tuple.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.common; - -public class Tuple { - - private L left; - private M mid; - private R right; - - public Tuple(L left, M mid, R right) { - this.left = left; - this.mid = mid; - this.right = right; - } - - public static Tuple of(final L left, final M mid, final R right) { - return new Tuple<>(left, mid, right); - } - - public L getLeft() { - return left; - } - - public void setLeft(L left) { - this.left = left; - } - - public M getMid() { - return mid; - } - - public void setMid(M mid) { - this.mid = mid; - } - - public R getRight() { - return right; - } - - public void setRight(R right) { - this.right = right; - } -} \ No newline at end of file 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 deleted file mode 100644 index 31757898..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/data/DataStorageInterface.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.data; - -import net.momirealms.customfishing.api.data.user.OfflineUser; - -import java.util.Collection; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; - -public interface DataStorageInterface { - - /** - * Initialize the data resource - */ - void initialize(); - - /** - * Close the data resource - */ - void disable(); - - /** - * Get the storage data source type - * - * @return {@link StorageType} - */ - StorageType getStorageType(); - - /** - * Retrieve a player's data - * - * @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. - */ - CompletableFuture> getPlayerData(UUID uuid, boolean lock); - - /** - * Update a player's data - * - * @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. - */ - CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData, boolean unlock); - - /** - * 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. - */ - CompletableFuture updateOrInsertPlayerData(UUID uuid, PlayerData playerData, boolean unlock); - - /** - * Update data for multiple players - * - * @param users A collection of OfflineUser objects representing players. - * @param unlock Whether to unlock the player data after updating. - */ - void updateManyPlayersData(Collection users, boolean unlock); - - /** - * 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. - */ - void lockOrUnlockPlayerData(UUID uuid, boolean lock); - - /** - * Get a set of unique user UUIDs - * - * @param legacy Whether to include legacy data in the retrieval. - * @return A set of unique user UUIDs. - */ - Set getUniqueUsers(boolean legacy); -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/data/PlayerData.java b/api/src/main/java/net/momirealms/customfishing/api/data/PlayerData.java deleted file mode 100644 index 78e1c7b5..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/data/PlayerData.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.data; - -import com.google.gson.annotations.SerializedName; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class PlayerData { - - @SerializedName("name") - protected String name; - @SerializedName("stats") - protected StatisticData statisticsData; - @SerializedName("bag") - protected InventoryData bagData; - @SerializedName("trade") - protected EarningData earningData; - - public static PlayerData LOCKED = empty(); - - public static Builder builder() { - return new Builder(); - } - - public static PlayerData empty() { - return new Builder() - .setBagData(InventoryData.empty()) - .setEarningData(EarningData.empty()) - .setStats(StatisticData.empty()) - .build(); - } - - public static class Builder { - - private final PlayerData playerData; - - public Builder() { - this.playerData = new PlayerData(); - } - - @NotNull - public Builder setName(@Nullable String name) { - this.playerData.name = name; - return this; - } - - @NotNull - public Builder setStats(@Nullable StatisticData statisticsData) { - this.playerData.statisticsData = statisticsData; - return this; - } - - @NotNull - public Builder setBagData(@Nullable InventoryData inventoryData) { - this.playerData.bagData = inventoryData; - return this; - } - - @NotNull - public Builder setEarningData(@Nullable EarningData earningData) { - this.playerData.earningData = earningData; - return this; - } - - @NotNull - public PlayerData build() { - return this.playerData; - } - } - - public StatisticData getStatistics() { - return statisticsData; - } - - public InventoryData getBagData() { - return bagData; - } - - public EarningData getEarningData() { - return earningData; - } - - public String getName() { - return name; - } - - public boolean isLocked() { - return this == LOCKED; - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/data/user/OfflineUser.java b/api/src/main/java/net/momirealms/customfishing/api/data/user/OfflineUser.java deleted file mode 100644 index 2322379c..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/data/user/OfflineUser.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.data.user; - -import net.momirealms.customfishing.api.data.EarningData; -import net.momirealms.customfishing.api.data.PlayerData; -import net.momirealms.customfishing.api.mechanic.bag.FishingBagHolder; -import net.momirealms.customfishing.api.mechanic.statistic.Statistics; - -import java.util.UUID; - -public interface OfflineUser { - - /** - * Get the username - * - * @return user name - */ - String getName(); - - /** - * Get the user's uuid - * - * @return uuid - */ - UUID getUUID(); - - /** - * Get the fishing bag holder - * - * @return fishing bag holder - */ - FishingBagHolder getHolder(); - - /** - * Get the player's earning data - * - * @return earning data - */ - EarningData getEarningData(); - - /** - * Get the player's statistics - * - * @return statistics - */ - Statistics getStatistics(); - - /** - * If the user is online on current server - * - * @return online or not - */ - boolean isOnline(); - - /** - * Get the data in another minimized format that can be saved - * - * @return player data - */ - PlayerData getPlayerData(); -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/event/CustomFishingReloadEvent.java b/api/src/main/java/net/momirealms/customfishing/api/event/CustomFishingReloadEvent.java index 1219d485..41defa68 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/event/CustomFishingReloadEvent.java +++ b/api/src/main/java/net/momirealms/customfishing/api/event/CustomFishingReloadEvent.java @@ -17,7 +17,7 @@ package net.momirealms.customfishing.api.event; -import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; @@ -25,9 +25,9 @@ import org.jetbrains.annotations.NotNull; public class CustomFishingReloadEvent extends Event { private static final HandlerList handlerList = new HandlerList(); - private final CustomFishingPlugin plugin; + private final BukkitCustomFishingPlugin plugin; - public CustomFishingReloadEvent(CustomFishingPlugin plugin) { + public CustomFishingReloadEvent(BukkitCustomFishingPlugin plugin) { this.plugin = plugin; } @@ -41,7 +41,7 @@ public class CustomFishingReloadEvent extends Event { return getHandlerList(); } - public CustomFishingPlugin getPluginInstance() { + public BukkitCustomFishingPlugin getPluginInstance() { return plugin; } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/event/FishHookLandEvent.java b/api/src/main/java/net/momirealms/customfishing/api/event/FishHookLandEvent.java deleted file mode 100644 index 8adeace1..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/event/FishHookLandEvent.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.event; - -import net.momirealms.customfishing.api.mechanic.effect.Effect; -import org.bukkit.entity.FishHook; -import org.bukkit.entity.Player; -import org.bukkit.event.HandlerList; -import org.bukkit.event.player.PlayerEvent; -import org.jetbrains.annotations.NotNull; - -/** - * This class represents an event that occurs when a fishing hook lands in either lava or water. - */ -public class FishHookLandEvent extends PlayerEvent { - - private static final HandlerList handlerList = new HandlerList(); - private final Target target; - private final FishHook fishHook; - private final Effect effect; - private boolean isFirst; - - /** - * Constructs a new FishHookLandEvent. - * - * @param who The player who triggered the event. - * @param target The target where the fishing hook has landed (LAVA or WATER). - * @param hook The fishing hook entity. - * @param initialEffect The initial effect - */ - public FishHookLandEvent(@NotNull Player who, Target target, FishHook hook, boolean isFirst, Effect initialEffect) { - super(who); - this.target = target; - this.fishHook = hook; - this.effect = initialEffect; - this.isFirst = isFirst; - } - - /** - * Gets the target where the fishing hook has landed. - * - * @return The target, which can be either LAVA or WATER. - */ - public Target getTarget() { - return target; - } - - /** - * Gets the fish hook bukkit entity - * - * @return fish hook - */ - public FishHook getFishHook() { - return fishHook; - } - - public static HandlerList getHandlerList() { - return handlerList; - } - - /** - * Is the first try of one fishing catch - * - * @return is first try - */ - public boolean isFirst() { - return isFirst; - } - - /** - * Get the fishing effect - * It's not advised to modify this value without checking "isFirst()" since this event can be trigger multiple times in one fishing catch - * - * @return fishing effect - */ - public Effect getEffect() { - return effect; - } - - @NotNull - @Override - public HandlerList getHandlers() { - return getHandlerList(); - } - - public enum Target { - LAVA, - WATER - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/event/FishingLootPreSpawnEvent.java b/api/src/main/java/net/momirealms/customfishing/api/event/FishingHookStateEvent.java similarity index 56% rename from api/src/main/java/net/momirealms/customfishing/api/event/FishingLootPreSpawnEvent.java rename to api/src/main/java/net/momirealms/customfishing/api/event/FishingHookStateEvent.java index 41409832..cde4df5c 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/event/FishingLootPreSpawnEvent.java +++ b/api/src/main/java/net/momirealms/customfishing/api/event/FishingHookStateEvent.java @@ -17,52 +17,46 @@ package net.momirealms.customfishing.api.event; -import org.bukkit.Location; +import org.bukkit.entity.FishHook; import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; -import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -public class FishingLootPreSpawnEvent extends PlayerEvent implements Cancellable { +public class FishingHookStateEvent extends PlayerEvent { private static final HandlerList handlerList = new HandlerList(); - private final Location location; - private final ItemStack itemStack; - private boolean isCancelled; + private final FishHook fishHook; + private final State state; - public FishingLootPreSpawnEvent(@NotNull Player who, Location location, ItemStack itemStack) { + public FishingHookStateEvent(@NotNull Player who, FishHook hook, State state) { super(who); - this.itemStack = itemStack; - this.location = location; - this.isCancelled = false; - } - - @Override - public boolean isCancelled() { - return isCancelled; - } - - @Override - public void setCancelled(boolean cancel) { - isCancelled = cancel; - } - - public Location getLocation() { - return location; - } - - public ItemStack getItemStack() { - return itemStack; - } - - @Override - public @NotNull HandlerList getHandlers() { - return handlerList; + this.fishHook = hook; + this.state = state; } public static HandlerList getHandlerList() { return handlerList; } + + public FishHook getFishHook() { + return fishHook; + } + + public State getState() { + return state; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return getHandlerList(); + } + + public enum State { + BITE, + ESCAPE, + LURE, + LAND + } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/event/FishingLootSpawnEvent.java b/api/src/main/java/net/momirealms/customfishing/api/event/FishingLootSpawnEvent.java index 0e067137..ecaadf5f 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/event/FishingLootSpawnEvent.java +++ b/api/src/main/java/net/momirealms/customfishing/api/event/FishingLootSpawnEvent.java @@ -17,44 +17,73 @@ package net.momirealms.customfishing.api.event; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.loot.Loot; import org.bukkit.Location; -import org.bukkit.entity.Item; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class FishingLootSpawnEvent extends PlayerEvent implements Cancellable { +public class FishingLootSpawnEvent extends PlayerEvent { private static final HandlerList handlerList = new HandlerList(); private final Location location; - private final Item item; - private boolean isCancelled; + private final Entity entity; + private final Loot loot; + private final Context context; + private boolean skipActions; + private boolean summonEntity; - public FishingLootSpawnEvent(@NotNull Player who, Location location, Item item) { - super(who); - this.item = item; + public FishingLootSpawnEvent(@NotNull Context context, Location location, Loot loot, @Nullable Entity entity) { + super(context.getHolder()); + this.entity = entity; + this.loot = loot; this.location = location; - this.isCancelled = false; + this.skipActions = false; + this.summonEntity = true; + this.context = context; } - @Override - public boolean isCancelled() { - return isCancelled; - } - - @Override - public void setCancelled(boolean cancel) { - isCancelled = cancel; + public Context getContext() { + return context; } public Location getLocation() { return location; } - public Item getItem() { - return item; + /** + * Get the loot entity + * + * @return entity + */ + @Nullable + public Entity getEntity() { + return entity; + } + + @NotNull + public Loot getLoot() { + return loot; + } + + public boolean summonEntity() { + return summonEntity; + } + + public void summonEntity(boolean summonEntity) { + this.summonEntity = summonEntity; + } + + public boolean skipActions() { + return skipActions; + } + + public void skipActions(boolean skipActions) { + this.skipActions = skipActions; } @Override diff --git a/api/src/main/java/net/momirealms/customfishing/api/event/FishingResultEvent.java b/api/src/main/java/net/momirealms/customfishing/api/event/FishingResultEvent.java index 2b2b96ee..2dd64e43 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/event/FishingResultEvent.java +++ b/api/src/main/java/net/momirealms/customfishing/api/event/FishingResultEvent.java @@ -17,6 +17,8 @@ package net.momirealms.customfishing.api.event; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; import net.momirealms.customfishing.api.mechanic.loot.Loot; import org.bukkit.entity.FishHook; import org.bukkit.entity.Player; @@ -24,14 +26,9 @@ import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.Map; import java.util.Optional; -/** - * This class represents an event that occurs when a player gets a result from fishing. - */ public class FishingResultEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlerList = new HandlerList(); @@ -39,21 +36,12 @@ public class FishingResultEvent extends PlayerEvent implements Cancellable { private final Result result; private final Loot loot; private final FishHook fishHook; - private final Map args; + private Context context; - /** - * Constructs a new FishingResultEvent. - * - * @param who The player who triggered the event. - * @param result The result of the fishing action (SUCCESS or FAILURE). - * @param loot The loot received from fishing. - * @param args A map of placeholders and their corresponding values. - */ - public FishingResultEvent(@NotNull Player who, Result result, FishHook fishHook, Loot loot, Map args) { - super(who); + public FishingResultEvent(@NotNull Context context, Result result, FishHook fishHook, Loot loot) { + super(context.getHolder()); this.result = result; this.loot = loot; - this.args = args; this.fishHook = fishHook; } @@ -77,87 +65,31 @@ public class FishingResultEvent extends PlayerEvent implements Cancellable { isCancelled = cancel; } - /** - * Gets the value associated with a specific argument key. - * Usage example event.getArg("{x}") - * - * @param key The argument key enclosed in curly braces, e.g., "{amount}". - * @return The value associated with the argument key, or null if not found. - */ - public String getArg(String key) { - return args.get(key); - } - - /** - * Set the value associated with a specific argument key. - * @param key key - * @param value value - * @return previous value - */ - @Nullable - public String setArg(String key, String value) { - return args.put(key, value); - } - - /** - * Gets the result of the fishing action. - * - * @return The fishing result, which can be either SUCCESS or FAILURE. - */ public Result getResult() { return result; } - /** - * Get the fish hook entity. - * - * @return fish hook - */ public FishHook getFishHook() { return fishHook; } - /** - * Gets the loot received from fishing. - * - * @return The loot obtained from the fishing action. - */ public Loot getLoot() { return loot; } - /** - * Gets the amount of loot received. - * This value is determined by the "multiple-loot" effect. - * If you want to get the amount of item spawned, listen to FishingLootSpawnEvent - * - * @return The amount of loot received, or 1 if the loot is block or entity - */ - public int getAmount() { - return Integer.parseInt(Optional.ofNullable(getArg("{amount}")).orElse("1")); - } - - /** - * Set the loot amount (Only works for items) - * - * @param amount amount - */ - public void setAmount(int amount) { - setArg("{amount}", String.valueOf(amount)); - } - - /** - * Set the score to get in competition - * - * @param score score - */ public void setScore(double score) { - setArg("{SCORE}", String.valueOf(score)); + context.arg(ContextKeys.CUSTOM_SCORE, score); + } + + public Context getContext() { + return context; + } + + public int getAmount() { + if (result == Result.FAILURE) return 0; + return Optional.ofNullable(context.arg(ContextKeys.AMOUNT)).orElse(1); } - /** - * An enumeration representing possible fishing results (SUCCESS or FAILURE). - */ public enum Result { SUCCESS, FAILURE diff --git a/api/src/main/java/net/momirealms/customfishing/api/event/LavaFishingEvent.java b/api/src/main/java/net/momirealms/customfishing/api/event/LavaFishingEvent.java deleted file mode 100644 index bc961c17..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/event/LavaFishingEvent.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.event; - -import org.bukkit.entity.FishHook; -import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.HandlerList; -import org.bukkit.event.player.PlayerEvent; -import org.jetbrains.annotations.NotNull; - -/** - * This class represents an event that occurs when a player fishes in lava. - */ -public class LavaFishingEvent extends PlayerEvent implements Cancellable { - - private static final HandlerList handlerList = new HandlerList(); - private final State state; - private boolean isCancelled; - private final FishHook hook; - - /** - * Constructs a new LavaFishingEvent. - * - * @param who The player who triggered the event. - * @param state The state of the fishing action (REEL_IN, CAUGHT_FISH, or BITE). - * @param hook The FishHook entity associated with the fishing action. - */ - public LavaFishingEvent(@NotNull Player who, State state, FishHook hook) { - super(who); - this.state = state; - this.isCancelled = false; - this.hook = hook; - } - - /** - * Gets the state of the fishing action. - * - * @return The fishing state, which can be REEL_IN, CAUGHT_FISH, or BITE. - */ - public State getState() { - return state; - } - - /** - * Gets the FishHook entity associated with the fishing action. - * - * @return The FishHook entity used in the fishing action. - */ - public FishHook getHook() { - return hook; - } - - public static HandlerList getHandlerList() { - return handlerList; - } - - @NotNull - @Override - public HandlerList getHandlers() { - return getHandlerList(); - } - - @Override - public boolean isCancelled() { - return isCancelled; - } - - @Override - public void setCancelled(boolean cancel) { - isCancelled = cancel; - } - - /** - * An enumeration representing possible states of the fishing action (REEL_IN, CAUGHT_FISH, BITE). - */ - public enum State { - REEL_IN, - CAUGHT_FISH, - BITE - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/event/RodCastEvent.java b/api/src/main/java/net/momirealms/customfishing/api/event/RodCastEvent.java index 7d496973..6ae4b1b2 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/event/RodCastEvent.java +++ b/api/src/main/java/net/momirealms/customfishing/api/event/RodCastEvent.java @@ -17,8 +17,7 @@ package net.momirealms.customfishing.api.event; -import net.momirealms.customfishing.api.mechanic.condition.FishingPreparation; -import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.fishing.FishingGears; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; @@ -30,24 +29,15 @@ import org.jetbrains.annotations.NotNull; */ public class RodCastEvent extends PlayerEvent implements Cancellable { - private final Effect effect; + private final FishingGears gears; private boolean isCancelled; private final PlayerFishEvent event; - private final FishingPreparation preparation; private static final HandlerList handlerList = new HandlerList(); - /** - * Constructs a new RodCastEvent. - * - * @param event The original PlayerFishEvent that triggered the rod cast. - * @param fishingPreparation The fishing preparation associated with the rod cast. - * @param effect The effect associated with the fishing rod cast. - */ - public RodCastEvent(PlayerFishEvent event, FishingPreparation fishingPreparation, Effect effect) { + public RodCastEvent(PlayerFishEvent event, FishingGears gears) { super(event.getPlayer()); - this.effect = effect; + this.gears = gears; this.event = event; - this.preparation = fishingPreparation; } @Override @@ -56,9 +46,10 @@ public class RodCastEvent extends PlayerEvent implements Cancellable { } /** - * Cancelling this event would not cancel the bukkit PlayerFishEvent + * Cancelling this event would disable CustomFishing mechanics + * If you want to prevent players from casting, use {@link #getBukkitPlayerFishEvent()} instead * - * @param cancel true if you wish to cancel this event + * @param cancel true if you want to cancel this event */ @Override public void setCancelled(boolean cancel) { @@ -69,15 +60,6 @@ public class RodCastEvent extends PlayerEvent implements Cancellable { return handlerList; } - /** - * Gets the fishing preparation associated with the rod cast. - * - * @return The FishingPreparation associated with the rod cast. - */ - public FishingPreparation getPreparation() { - return preparation; - } - @NotNull @Override public HandlerList getHandlers() { @@ -85,16 +67,16 @@ public class RodCastEvent extends PlayerEvent implements Cancellable { } /** - * Gets the effect associated with the fishing rod cast. + * Get the {@link FishingGears} * - * @return The Effect associated with the rod cast. + * @return fishing gears */ - public Effect getEffect() { - return effect; + public FishingGears getGears() { + return gears; } /** - * Gets the original PlayerFishEvent that triggered the rod cast. + * Gets the original PlayerFishEvent that triggered the {@link RodCastEvent}. * * @return The original PlayerFishEvent. */ diff --git a/api/src/main/java/net/momirealms/customfishing/api/integration/BlockProvider.java b/api/src/main/java/net/momirealms/customfishing/api/integration/BlockProvider.java new file mode 100644 index 00000000..f0bed0fd --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/integration/BlockProvider.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.integration; + +import net.momirealms.customfishing.api.mechanic.block.BlockDataModifier; +import net.momirealms.customfishing.api.mechanic.context.Context; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Interface for providing custom block data and retrieving block IDs within the CustomFishing plugin. + * Extends the ExternalProvider interface. + */ +public interface BlockProvider extends ExternalProvider { + + /** + * Generates BlockData for a given player based on a block ID and a list of modifiers. + * + * @param context The player for whom the block data is generated. + * @param id The unique identifier for the block. + * @param modifiers A list of {@link BlockDataModifier} objects to apply to the block data. + * @return The generated {@link BlockData} for the specified block ID and modifiers. + */ + BlockData blockData(@NotNull Context context, @NotNull String id, List modifiers); + + /** + * Retrieves the unique block ID associated with a given block. + * + * @param block The block for which the ID is to be retrieved. + * @return The unique block ID as a string, or null if no ID is associated with the block. + */ + @Nullable + String blockID(@NotNull Block block); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/integration/EnchantmentInterface.java b/api/src/main/java/net/momirealms/customfishing/api/integration/EnchantmentProvider.java similarity index 80% rename from api/src/main/java/net/momirealms/customfishing/api/integration/EnchantmentInterface.java rename to api/src/main/java/net/momirealms/customfishing/api/integration/EnchantmentProvider.java index 04a92876..d0daba40 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/integration/EnchantmentInterface.java +++ b/api/src/main/java/net/momirealms/customfishing/api/integration/EnchantmentProvider.java @@ -17,19 +17,19 @@ package net.momirealms.customfishing.api.integration; +import net.momirealms.customfishing.common.util.Pair; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import java.util.List; -public interface EnchantmentInterface { +public interface EnchantmentProvider extends ExternalProvider { /** * Get a list of enchantments with level for itemStack - * format: plugin:enchantment:level - * example: minecraft:sharpness:5 * * @param itemStack itemStack * @return enchantment list */ - List getEnchants(ItemStack itemStack); + List> getEnchants(@NotNull ItemStack itemStack); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/integration/EntityProvider.java b/api/src/main/java/net/momirealms/customfishing/api/integration/EntityProvider.java new file mode 100644 index 00000000..9cfea439 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/integration/EntityProvider.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.integration; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +public interface EntityProvider extends ExternalProvider { + + /** + * Spawns an entity at the specified location with the given properties. + * + * @param location The location where the entity will be spawned. + * @param id The identifier of the entity to be spawned. + * @param propertyMap A map containing additional properties for the entity. + * @return The spawned entity. + */ + @NotNull + Entity spawn(@NotNull Location location, @NotNull String id, @NotNull Map propertyMap); + + default Entity spawn(@NotNull Location location, @NotNull String id) { + return spawn(location, id, new HashMap<>()); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/data/user/OnlineUser.java b/api/src/main/java/net/momirealms/customfishing/api/integration/ExternalProvider.java similarity index 74% rename from api/src/main/java/net/momirealms/customfishing/api/data/user/OnlineUser.java rename to api/src/main/java/net/momirealms/customfishing/api/integration/ExternalProvider.java index a26fe65a..32cd5667 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/data/user/OnlineUser.java +++ b/api/src/main/java/net/momirealms/customfishing/api/integration/ExternalProvider.java @@ -15,16 +15,14 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.data.user; +package net.momirealms.customfishing.api.integration; -import org.bukkit.entity.Player; - -public interface OnlineUser extends OfflineUser { +public interface ExternalProvider { /** - * Get the bukkit player + * Gets the identification of the external provider. * - * @return player + * @return The identification string of the external provider. */ - Player getPlayer(); + String identifier(); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/integration/IntegrationManager.java b/api/src/main/java/net/momirealms/customfishing/api/integration/IntegrationManager.java new file mode 100644 index 00000000..6468d47b --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/integration/IntegrationManager.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.integration; + +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import net.momirealms.customfishing.common.util.Pair; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Interface for managing integration providers in the custom fishing API. + * This allows for the registration and retrieval of various types of providers + * such as Leveler, Enchantment, and Season providers. + */ +public interface IntegrationManager extends Reloadable { + + /** + * Registers a LevelerProvider. + * + * @param levelerProvider the LevelerProvider to register + * @return true if registration is successful, false otherwise + */ + boolean registerLevelerProvider(@NotNull LevelerProvider levelerProvider); + + /** + * Unregisters a LevelerProvider by its ID. + * + * @param id the ID of the LevelerProvider to unregister + * @return true if unregistration is successful, false otherwise + */ + boolean unregisterLevelerProvider(@NotNull String id); + + /** + * Registers an EnchantmentProvider. + * + * @param enchantmentProvider the EnchantmentProvider to register + * @return true if registration is successful, false otherwise + */ + boolean registerEnchantmentProvider(@NotNull EnchantmentProvider enchantmentProvider); + + /** + * Unregisters an EnchantmentProvider by its ID. + * + * @param id the ID of the EnchantmentProvider to unregister + * @return true if unregistration is successful, false otherwise + */ + boolean unregisterEnchantmentProvider(@NotNull String id); + + /** + * Registers a SeasonProvider. + * + * @param seasonProvider the SeasonProvider to register + * @return true if registration is successful, false otherwise + */ + boolean registerSeasonProvider(@NotNull SeasonProvider seasonProvider); + + /** + * Unregisters the SeasonProvider. + * + * @return true if unregistration is successful, false otherwise + */ + boolean unregisterSeasonProvider(); + + boolean registerEntityProvider(@NotNull EntityProvider entityProvider); + + boolean unregisterEntityProvider(@NotNull String id); + + /** + * Retrieves a registered LevelerProvider by its ID. + * + * @param id the ID of the LevelerProvider to retrieve + * @return the LevelerProvider if found, or null if not found + */ + @Nullable + LevelerProvider getLevelerProvider(String id); + + /** + * Retrieves a registered EnchantmentProvider by its ID. + * + * @param id the ID of the EnchantmentProvider to retrieve + * @return the EnchantmentProvider if found, or null if not found + */ + @Nullable + EnchantmentProvider getEnchantmentProvider(String id); + + /** + * Retrieves a registered SeasonProvider by its ID. + * + * @return the SeasonProvider if found, or null if not found + */ + @Nullable + SeasonProvider getSeasonProvider(); + + List> getEnchantments(ItemStack itemStack); + + boolean registerItemProvider(@NotNull ItemProvider itemProvider); + + boolean unregisterItemProvider(@NotNull String id); + + boolean registerBlockProvider(@NotNull BlockProvider block); + + boolean unregisterBlockProvider(@NotNull String id); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/integration/ItemProvider.java b/api/src/main/java/net/momirealms/customfishing/api/integration/ItemProvider.java new file mode 100644 index 00000000..e5a081d1 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/integration/ItemProvider.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.integration; + +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Interface representing a provider for custom items in the Custom Fishing plugin. + * This interface allows for building items for players and retrieving item IDs from item stacks. + */ +public interface ItemProvider extends ExternalProvider { + + /** + * Builds an ItemStack for a player based on a specified item ID. + * + * @param player the player for whom the item is being built. + * @param id the ID of the item to build. + * @return the built ItemStack. + */ + @NotNull + ItemStack buildItem(@NotNull Player player, @NotNull String id); + + /** + * Retrieves the item ID from a given ItemStack. + * + * @param itemStack the ItemStack from which to retrieve the item ID. + * @return the item ID as a string, or null if the item stack does not have an associated ID. + */ + @Nullable + String itemID(@NotNull ItemStack itemStack); +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/integration/LevelInterface.java b/api/src/main/java/net/momirealms/customfishing/api/integration/LevelerProvider.java similarity index 82% rename from api/src/main/java/net/momirealms/customfishing/api/integration/LevelInterface.java rename to api/src/main/java/net/momirealms/customfishing/api/integration/LevelerProvider.java index da326178..0fbe15bc 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/integration/LevelInterface.java +++ b/api/src/main/java/net/momirealms/customfishing/api/integration/LevelerProvider.java @@ -18,8 +18,9 @@ package net.momirealms.customfishing.api.integration; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public interface LevelInterface { +public interface LevelerProvider extends ExternalProvider { /** * Add exp to a certain skill or job @@ -28,7 +29,7 @@ public interface LevelInterface { * @param target the skill or job, for instance "Fishing" "fisherman" * @param amount the exp amount */ - void addXp(Player player, String target, double amount); + void addXp(@NotNull Player player, @NotNull String target, double amount); /** * Get a player's skill or job's level @@ -37,5 +38,5 @@ public interface LevelInterface { * @param target the skill or job, for instance "Fishing" "fisherman" * @return level */ - int getLevel(Player player, String target); + int getLevel(@NotNull Player player, @NotNull String target); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/integration/SeasonInterface.java b/api/src/main/java/net/momirealms/customfishing/api/integration/SeasonProvider.java similarity index 83% rename from api/src/main/java/net/momirealms/customfishing/api/integration/SeasonInterface.java rename to api/src/main/java/net/momirealms/customfishing/api/integration/SeasonProvider.java index bae8da60..526a0854 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/integration/SeasonInterface.java +++ b/api/src/main/java/net/momirealms/customfishing/api/integration/SeasonProvider.java @@ -17,10 +17,11 @@ package net.momirealms.customfishing.api.integration; +import net.momirealms.customfishing.api.mechanic.misc.season.Season; import org.bukkit.World; import org.jetbrains.annotations.NotNull; -public interface SeasonInterface { +public interface SeasonProvider extends ExternalProvider { /** * Get a world's season @@ -28,5 +29,6 @@ public interface SeasonInterface { * @param world world * @return spring, summer, autumn, winter or disabled */ - @NotNull String getSeason(World world); + @NotNull + Season getSeason(@NotNull World world); } 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 deleted file mode 100644 index 334bd6bf..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -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; - -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); - - /** - * 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. - */ - static void triggerActions(Condition condition, Action... actions) { - if (actions != null) - for (Action action : actions) - action.trigger(condition); - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/AdventureManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/AdventureManager.java deleted file mode 100644 index 1f7c9c0b..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/AdventureManager.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.kyori.adventure.text.Component; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -public interface AdventureManager { - - /** - * Get component from text - * @param text text - * @return component - */ - Component getComponentFromMiniMessage(String text); - - /** - * Send a message to a command sender - * @param sender sender - * @param msg message - */ - void sendMessage(CommandSender sender, String msg); - - /** - * Send a message with prefix - * - * @param sender command sender - * @param s message - */ - void sendMessageWithPrefix(CommandSender sender, String s); - - /** - * Send a message to console - * @param msg message - */ - void sendConsoleMessage(String msg); - - /** - * Send a message to a player - * @param player player - * @param msg message - */ - void sendPlayerMessage(Player player, String msg); - - /** - * Send a title to a player - * @param player player - * @param title title - * @param subtitle subtitle - * @param in in (ms) - * @param duration duration (ms) - * @param out out (ms) - */ - void sendTitle(Player player, String title, String subtitle, int in, int duration, int out); - - /** - * Send a title to a player - * @param player player - * @param title title - * @param subtitle subtitle - * @param in in (ms) - * @param duration duration (ms) - * @param out out (ms) - */ - void sendTitle(Player player, Component title, Component subtitle, int in, int duration, int out); - - /** - * Send actionbar - * @param player player - * @param msg msg - */ - void sendActionbar(Player player, String msg); - - /** - * Play a sound to a player - * @param player player - * @param source sound source - * @param key sound key - * @param volume volume - * @param pitch pitch - */ - void sendSound(Player player, Sound.Source source, Key key, float volume, float pitch); - - void sendSound(Player player, Sound sound); - - /** - * Replace legacy color codes to MiniMessage format - * @param legacy legacy text - * @return MiniMessage format text - */ - String legacyToMiniMessage(String legacy); - - /** - * if a char is legacy color code - * @param c char - * @return is legacy color - */ - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - boolean isColorCode(char c); - - /** - * Get legacy format text - * @param component component - * @return legacy format text - */ - String componentToLegacy(Component component); - - /** - * Get json - * @param component component - * @return json - */ - String componentToJson(Component component); - - /** - * Get paper component - * @param component shaded component - * @return paper component - */ - Object shadedComponentToOriginalComponent(Component component); -} 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 deleted file mode 100644 index f83c36d2..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/BagManager.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -import net.momirealms.customfishing.api.data.user.OfflineUser; -import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.requirement.Requirement; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; - -import java.util.List; -import java.util.UUID; - -public interface BagManager { - - /** - * Is bag enabled - * - * @return enabled or not - */ - 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); - - /** - * Get the rows of a player's fishing bag - * - * @param player player who owns the bag - * @return rows - */ - int getBagInventoryRows(Player player); - - /** - * 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); - - /** - * Get the actions to perform when loot is automatically collected - * - * @return actions - */ - Action[] getCollectLootActions(); - - /** - * Get the actions to perform when bag is full - * - * @return actions - */ - Action[] getBagFullActions(); - - /** - * If bag can store fishing loots - * - * @return can store or not - */ - boolean doesBagStoreLoots(); - - /** - * Get the fishing bag's title - * - * @return title - */ - String getBagTitle(); - - /** - * Get a list of allowed items in bag - * - * @return whitelisted items - */ - List getBagWhiteListItems(); - - /** - * Get the requirements for automatically collecting loots - * - * @return requirements - */ - Requirement[] getCollectRequirements(); -} 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 deleted file mode 100644 index baa69366..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/BlockManager.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -import net.momirealms.customfishing.api.mechanic.block.BlockDataModifierBuilder; -import net.momirealms.customfishing.api.mechanic.block.BlockLibrary; -import net.momirealms.customfishing.api.mechanic.block.BlockStateModifierBuilder; -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 { - - /** - * 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); - - /** - * 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); - - /** - * 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 deleted file mode 100644 index d78ade4a..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/CompetitionManager.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -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; - -public interface CompetitionManager { - - /** - * Retrieves a set of all competition names. - * - * @return A set of competition names. - */ - @NotNull Set getAllCompetitionKeys(); - - /** - * 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); - - /** - * 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 serverGroup 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, @Nullable String serverGroup); - - /** - * 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 serverGroup 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, @Nullable String serverGroup); - - /** - * Gets the number of seconds until the next competition. - * - * @return The number of seconds until the next competition. - */ - int getNextCompetitionSeconds(); - - /** - * 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 deleted file mode 100644 index caccbfd4..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/EffectManager.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -import net.momirealms.customfishing.api.common.Key; -import net.momirealms.customfishing.api.mechanic.effect.BaseEffect; -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 { - - /** - * 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); - - /** - * 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); - - /** - * 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); - - /** - * 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); - - BaseEffect getBaseEffect(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 deleted file mode 100644 index 25de6e97..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/EntityManager.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -import net.momirealms.customfishing.api.mechanic.entity.EntityLibrary; -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); - - /** - * 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 deleted file mode 100644 index bb9eabbe..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -import net.momirealms.customfishing.api.mechanic.TempFishingState; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.effect.Effect; -import net.momirealms.customfishing.api.mechanic.game.GameInstance; -import net.momirealms.customfishing.api.mechanic.game.GameSettings; -import net.momirealms.customfishing.api.mechanic.game.GamingPlayer; -import org.bukkit.entity.FishHook; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -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); - - /** - * 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); - - /** - * 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); - - /** - * 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. - */ - boolean 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. - */ - boolean 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/HookManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/HookManager.java deleted file mode 100644 index d0649926..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/HookManager.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -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 { - - /** - * 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 deleted file mode 100644 index c78a91e9..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/IntegrationManager.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -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); - - /** - * 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); - - /** - * 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); - - /** - * 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 deleted file mode 100644 index 25759e9c..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/ItemManager.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -import net.momirealms.customfishing.api.common.Key; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -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 org.bukkit.Location; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; -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); - - /** - * 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); - - /** - * 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); - - /** - * 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); - - /** - * 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); - - /** - * 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); - - /** - * 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(); - - /** - * 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); - - /** - * Get an itemStack's appearance (material + custom model data) - * - * @param player player - * @param material id - * @return appearance - */ - ItemStack getItemStackAppearance(Player player, String material); - - /** - * 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); - - /** - * 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); - - /** - * 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 itemStack The loot to drop - * @param condition A map of placeholders for item customization. - */ - void dropItem(Player player, Location hookLocation, Location playerLocation, ItemStack itemStack, Condition condition); - - /** - * 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(Player player, 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 deleted file mode 100644 index 75acc6f8..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/LootManager.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -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 effect 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 effect, 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 deleted file mode 100644 index 1388e503..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/MarketManager.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.Map; - -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 getCachedDate(); - - /** - * 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(); - - /** - * Calculates the price based on a formula with provided variables. - * - * @return The calculated price based on the formula and provided variables. - */ - double getFishPrice(Player player, Map vars); - - /** - * Gets the character representing the item slot in the MarketGUI. - * - * @return The item slot character. - */ - char getItemSlot(); - - /** - * Gets the character representing the sell slot in the MarketGUI. - * - * @return The sell slot character. - */ - char getSellSlot(); - - /** - * Gets the character representing the sell-all slot in the MarketGUI. - * - * @return The sell-all slot character. - */ - char getSellAllSlot(); - - /** - * 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(Player player); - - /** - * Is market enabled - * - * @return enable or not - */ - boolean isEnable(); - - /** - * Should fish in bag also be sold - * - * @return sell or not - */ - boolean sellFishingBag(); - - /** - * Get the total worth of the items in inventory - * - * @param inventory inventory - * @return total worth - */ - double getInventoryTotalWorth(Inventory inventory); - - int getInventorySellAmount(Inventory inventory); -} 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 deleted file mode 100644 index 922db18c..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/PlaceholderManager.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Map; - -public interface PlaceholderManager { - - /** - * Register a custom placeholder - * - * @param placeholder for instance {level} - * @param original for instance %player_level% - * @return success or not, it would fail if the placeholder has been registered - */ - boolean registerCustomPlaceholder(String placeholder, String original); - - /** - * 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); - - /** - * 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); - - /** - * Get an expression's value - * @param player player - * @param formula formula - * @param vars vars - * @return result - */ - double getExpressionValue(Player player, String formula, Map vars); - - /** - * Get an expression's value - * @param formula formula - * @return result - */ - double getExpressionValue(String formula); -} 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 deleted file mode 100644 index d18a6cd2..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/RequirementManager.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -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; - -public interface RequirementManager { - - /** - * Legacy format support - * @param key key - * @param requirements requirements - * @param weight weight - * @return success or not - */ - @Deprecated - boolean putLegacyLootToMap(String key, Requirement[] requirements, double weight); - - /** - * 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); - - /** - * 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); - - /** - * If a requirement type exists - * - * @param type type - * @return exists or not - */ - boolean hasRequirement(String type); - - /** - * 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); - - /** - * 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); - - /** - * 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); - - /** - * 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)) { - return false; - } - } - return true; - } -} 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 deleted file mode 100644 index 64ad6544..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/StatisticsManager.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -import net.momirealms.customfishing.api.mechanic.statistic.Statistics; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.UUID; - -public interface StatisticsManager { - - /** - * 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 deleted file mode 100644 index b84b5c1a..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/StorageManager.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -import net.momirealms.customfishing.api.data.DataStorageInterface; -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.Collection; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; - -public interface StorageManager { - - /** - * Gets the unique server identifier. - * - * @return The unique server identifier. - */ - @NotNull String getUniqueID(); - - /** - * 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. - */ - @Nullable OnlineUser getOnlineUser(UUID uuid); - - /** - * Get all the online users - * - * @return online users - */ - Collection getOnlineUsers(); - - /** - * 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); - - /** - * 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(); - - /** - * 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); - - /** - * 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 deleted file mode 100644 index 02c3dd9b..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/TotemManager.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.manager; - -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/GlobalSettings.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/GlobalSettings.java deleted file mode 100644 index 0192b1d9..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/GlobalSettings.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; -import org.bukkit.configuration.ConfigurationSection; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents global settings for actions related to fishing, loot, rods, and bait. - */ -public class GlobalSettings { - - private static HashMap lootActions = new HashMap<>(); - private static HashMap rodActions = new HashMap<>(); - private static HashMap baitActions = new HashMap<>(); - private static HashMap hookActions = new HashMap<>(); - private static EffectModifier[] effectModifiers; - - /** - * Loads global settings from a configuration section. - * - * @param section The configuration section to load settings from. - */ - public static void loadEvents(ConfigurationSection section) { - if (section == null) return; - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - HashMap map = CustomFishingPlugin.get().getActionManager().getActionMap(inner); - switch (entry.getKey()) { - case "loot" -> lootActions = map; - case "rod" -> rodActions = map; - case "bait" -> baitActions = map; - case "hook" -> hookActions = map; - } - } - } - } - - public static EffectModifier[] getEffectModifiers() { - return effectModifiers; - } - - public static void setEffects(EffectModifier[] modifiers) { - effectModifiers = modifiers; - } - - /** - * Unloads global settings, clearing all action maps. - */ - public static void unload() { - lootActions.clear(); - rodActions.clear(); - baitActions.clear(); - } - - /** - * Triggers loot-related actions for a specific trigger and condition. - * - * @param trigger The trigger to activate actions for. - * @param condition The condition that triggered the actions. - */ - public static void triggerLootActions(ActionTrigger trigger, Condition condition) { - Action[] actions = lootActions.get(trigger); - if (actions != null) { - for (Action action : actions) { - action.trigger(condition); - } - } - } - - /** - * Triggers rod-related actions for a specific trigger and condition. - * - * @param trigger The trigger to activate actions for. - * @param condition The condition that triggered the actions. - */ - public static void triggerRodActions(ActionTrigger trigger, Condition condition) { - Action[] actions = rodActions.get(trigger); - if (actions != null) { - for (Action action : actions) { - action.trigger(condition); - } - } - } - - /** - * Triggers bait-related actions for a specific trigger and condition. - * - * @param trigger The trigger to activate actions for. - * @param condition The condition that triggered the actions. - */ - public static void triggerBaitActions(ActionTrigger trigger, Condition condition) { - Action[] actions = baitActions.get(trigger); - if (actions != null) { - for (Action action : actions) { - action.trigger(condition); - } - } - } - - /** - * Triggers hook-related actions for a specific trigger and condition. - * - * @param trigger The trigger to activate actions for. - * @param condition The condition that triggered the actions. - */ - public static void triggerHookActions(ActionTrigger trigger, Condition condition) { - Action[] actions = hookActions.get(trigger); - if (actions != null) { - for (Action action : actions) { - action.trigger(condition); - } - } - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/MechanicType.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/MechanicType.java new file mode 100644 index 00000000..ca0ac3c9 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/MechanicType.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic; + +import net.kyori.adventure.util.Index; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; + +public class MechanicType { + + private static final HashMap> types = new HashMap<>(); + + public static final MechanicType LOOT = of("loot"); + public static final MechanicType ROD = of("rod"); + public static final MechanicType UTIL = of("util"); + public static final MechanicType BAIT = of("bait"); + public static final MechanicType HOOK = of("hook"); + public static final MechanicType TOTEM = of("totem"); + public static final MechanicType ENTITY = of("entity"); + public static final MechanicType BLOCK = of("block"); + public static final MechanicType ENCHANT = of("enchant"); + + private final String type; + + public MechanicType(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + private static MechanicType of(String type) { + return new MechanicType(type); + } + + public static MechanicType[] values() { + return new MechanicType[]{LOOT, ROD, UTIL, BAIT, HOOK, TOTEM, ENCHANT, ENTITY, BLOCK}; + } + + private static final Index INDEX = Index.create(MechanicType::getType, values()); + + public static Index index() { + return INDEX; + } + + @ApiStatus.Internal + public static void register(String id, MechanicType type) { + List previous = types.computeIfAbsent(id, k -> new ArrayList<>()); + previous.add(type); + } + + @Nullable + @ApiStatus.Internal + public static List getTypeByID(String id) { + return types.get(id); + } + + @ApiStatus.Internal + public static void reset() { + types.clear(); + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + MechanicType mechanicType = (MechanicType) object; + return Objects.equals(type, mechanicType.type); + } + + @Override + public int hashCode() { + return Objects.hashCode(type); + } + + @Override + public String toString() { + return type; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/TempFishingState.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/TempFishingState.java deleted file mode 100644 index 503ef51e..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/TempFishingState.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic; - -import net.momirealms.customfishing.api.mechanic.condition.FishingPreparation; -import net.momirealms.customfishing.api.mechanic.effect.Effect; -import net.momirealms.customfishing.api.mechanic.loot.Loot; - -/** - * Represents a temporary state during fishing that includes an effect, preparation, and loot. - */ -public class TempFishingState { - - private final Effect effect; - private final FishingPreparation preparation; - private Loot loot; - - /** - * Creates a new instance of TempFishingState. - * - * @param effect The effect associated with this state. - * @param preparation The fishing preparation associated with this state. - * @param loot The loot associated with this state. - */ - public TempFishingState(Effect effect, FishingPreparation preparation, Loot loot) { - this.effect = effect; - this.preparation = preparation; - this.loot = loot; - } - - /** - * Gets the effect associated with this fishing state. - * - * @return The effect. - */ - public Effect getEffect() { - return effect; - } - - /** - * Gets the fishing preparation associated with this fishing state. - * - * @return The fishing preparation. - */ - public FishingPreparation getPreparation() { - return preparation; - } - - /** - * Gets the loot associated with this fishing state. - * - * @return The loot. - */ - public Loot getLoot() { - return loot; - } - - /** - * Set the loot associated with this fishing state. - * - */ - public void setLoot(Loot loot) { - this.loot = loot; - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/Action.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/Action.java index af44d142..6276916d 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/Action.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/Action.java @@ -17,10 +17,14 @@ package net.momirealms.customfishing.api.mechanic.action; -import net.momirealms.customfishing.api.mechanic.condition.Condition; +import net.momirealms.customfishing.api.mechanic.context.Context; -public interface Action { - - void trigger(Condition condition); +public interface Action { + /** + * Triggers the action based on the provided condition. + * + * @param context the context + */ + void trigger(Context context); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionExpansion.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionExpansion.java index bd48cfd1..6fc0ff8c 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionExpansion.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionExpansion.java @@ -17,13 +17,39 @@ package net.momirealms.customfishing.api.mechanic.action; -public abstract class ActionExpansion { +/** + * Abstract class representing an expansion of an action in the custom fishing API. + * This class should be extended to provide specific implementations of actions. + * + * @param the type parameter for the action factory + */ +public abstract class ActionExpansion { + /** + * Retrieves the version of this action expansion. + * + * @return a String representing the version of the action expansion + */ public abstract String getVersion(); + /** + * Retrieves the author of this action expansion. + * + * @return a String representing the author of the action expansion + */ public abstract String getAuthor(); + /** + * Retrieves the type of this action. + * + * @return a String representing the type of action + */ public abstract String getActionType(); - public abstract ActionFactory getActionFactory(); + /** + * Retrieves the action factory associated with this action expansion. + * + * @return an ActionFactory of type T that creates instances of the action + */ + public abstract ActionFactory getActionFactory(); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionFactory.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionFactory.java index fdaae29d..a3d526e0 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionFactory.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionFactory.java @@ -17,7 +17,18 @@ package net.momirealms.customfishing.api.mechanic.action; -public interface ActionFactory { +/** + * Interface representing a factory for creating actions. + * + * @param the type of object that the action will operate on + */ +public interface ActionFactory { - Action build(Object args, double chance); + /** + * Constructs an action based on the provided arguments. + * + * @param args the args containing the arguments needed to build the action + * @return the constructed action + */ + Action process(Object args, double chance); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionManager.java new file mode 100644 index 00000000..9e724f29 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionManager.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.action; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * The ActionManager interface manages custom action types and provides methods for handling actions. + * + * @param the type of the context in which the actions are triggered. + */ +public interface ActionManager extends Reloadable { + + /** + * Registers a custom action type with its corresponding factory. + * + * @param type The type identifier of the action. + * @param actionFactory The factory responsible for creating instances of the action. + * @return True if registration was successful, false if the type is already registered. + */ + boolean registerAction(String type, ActionFactory actionFactory); + + /** + * Unregisters a custom action type. + * + * @param type The type identifier of the action to unregister. + * @return True if unregistration was successful, false if the type is not registered. + */ + boolean unregisterAction(String type); + + /** + * Checks if an action type is registered. + * + * @param type The type identifier of the action. + * @return True if the action type is registered, otherwise false. + */ + boolean hasAction(@NotNull String type); + + /** + * Retrieves the action factory for the specified action type. + * + * @param type The type identifier of the action. + * @return The action factory for the specified type, or null if no factory is found. + */ + @Nullable + ActionFactory getActionFactory(@NotNull String type); + + /** + * Parses an action from a configuration section. + * + * @param section The configuration section containing the action definition. + * @return The parsed action. + */ + Action parseAction(Section section); + + /** + * Parses an array of actions from a configuration section. + * + * @param section The configuration section containing the action definitions. + * @return An array of parsed actions. + */ + @NotNull + Action[] parseActions(Section section); + + /** + * Parses an action from the given type and arguments. + * + * @param type The type identifier of the action. + * @param args The arguments for the action. + * @return The parsed action. + */ + Action parseAction(@NotNull String type, @NotNull Object args); + + /** + * Generates a map of actions triggered by specific events from a configuration section. + * + * @param section The configuration section containing event-action mappings. + * @return A map where the keys are action triggers and the values are arrays of actions associated with those triggers. + */ + default Map[]> parseEventActions(Section section) { + HashMap[]> actionMap = new HashMap<>(); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + try { + actionMap.put( + ActionTrigger.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)), + parseActions(innerSection) + ); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } + } + } + return actionMap; + } + + /** + * Parses a configuration section to generate a map of timed actions. + * + * @param section The configuration section containing time-action mappings. + * @return A TreeMap where the keys are time values (in integer form) and the values are arrays of actions associated with those times. + */ + default TreeMap[]> parseTimesActions(Section section) { + TreeMap[]> actionMap = new TreeMap<>(); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + actionMap.put(Integer.parseInt(entry.getKey()), parseActions(innerSection)); + } + } + return actionMap; + } + + /** + * Triggers a list of actions with the given context. + * If the list of actions is not null, each action in the list is triggered. + * + * @param context The context associated with the actions. + * @param actions The list of actions to trigger. + */ + static void trigger(@NotNull Context context, @Nullable List> actions) { + if (actions != null) + for (Action action : actions) + action.trigger(context); + } + + /** + * Triggers an array of actions with the given context. + * If the array of actions is not null, each action in the array is triggered. + * + * @param context The context associated with the actions. + * @param actions The array of actions to trigger. + */ + static void trigger(@NotNull Context context, @Nullable Action[] actions) { + if (actions != null) + for (Action action : actions) + action.trigger(context); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionTrigger.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionTrigger.java index 2f41923a..3b981bb1 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionTrigger.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/ActionTrigger.java @@ -18,7 +18,6 @@ package net.momirealms.customfishing.api.mechanic.action; public enum ActionTrigger { - SUCCESS, FAILURE, HOOK, @@ -26,8 +25,11 @@ public enum ActionTrigger { CAST, BITE, LAND, + LURE, + ESCAPE, ACTIVATE, TIMER, INTERACT, + REEL, NEW_SIZE_RECORD } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/ParseUtils.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/EmptyAction.java similarity index 61% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/ParseUtils.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/action/EmptyAction.java index 3791dab0..cb819b05 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/ParseUtils.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/action/EmptyAction.java @@ -15,19 +15,20 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.papi; +package net.momirealms.customfishing.api.mechanic.action; -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.OfflinePlayer; +import net.momirealms.customfishing.api.mechanic.context.Context; import org.bukkit.entity.Player; -public class ParseUtils { +/** + * 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 String setPlaceholders(Player player, String text) { - return PlaceholderAPI.setPlaceholders(player, text); - } + public static final EmptyAction INSTANCE = new EmptyAction(); - public static String setPlaceholders(OfflinePlayer player, String text) { - return PlaceholderAPI.setPlaceholders(player, text); + @Override + public void trigger(Context context) { } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/VersionManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/bag/BagManager.java similarity index 56% rename from api/src/main/java/net/momirealms/customfishing/api/manager/VersionManager.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/bag/BagManager.java index d75bdfd1..5f01cf69 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/VersionManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/bag/BagManager.java @@ -15,27 +15,26 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.manager; +package net.momirealms.customfishing.api.mechanic.bag; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.bukkit.entity.Player; + +import java.util.UUID; import java.util.concurrent.CompletableFuture; -public interface VersionManager { +public interface BagManager extends Reloadable { - boolean isVersionNewerThan1_19(); + static int getBagInventoryRows(Player player) { + int size = 1; + for (int i = 6; i > 1; i--) { + if (player.hasPermission("fishingbag.rows." + i)) { + size = i; + break; + } + } + return size; + } - boolean isVersionNewerThan1_19_R3(); - - boolean isVersionNewerThan1_19_R2(); - - CompletableFuture checkUpdate(); - - boolean isVersionNewerThan1_20(); - - boolean isSpigot(); - - public boolean hasRegionScheduler(); - - String getPluginVersion(); - - boolean isMojmap(); + CompletableFuture openBag(Player viewer, UUID owner); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/bag/FishingBagHolder.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/bag/FishingBagHolder.java index 17d0644a..010fb62d 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/bag/FishingBagHolder.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/bag/FishingBagHolder.java @@ -17,6 +17,8 @@ package net.momirealms.customfishing.api.mechanic.bag; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; @@ -35,6 +37,20 @@ public class FishingBagHolder implements InventoryHolder { @Override public @NotNull Inventory getInventory() { + Player player = Bukkit.getPlayer(owner); + if (player != null) { + int rows = BagManager.getBagInventoryRows(player); + if (rows * 9 != inventory.getSize()) { + Inventory newBag = Bukkit.createInventory(this, rows * 9); + ItemStack[] newContents = new ItemStack[rows * 9]; + ItemStack[] oldContents = inventory.getContents(); + for (int i = 0; i < rows * 9 && i < oldContents.length; i++) { + newContents[i] = oldContents[i]; + } + newBag.setContents(newContents); + this.setInventory(newBag); + } + } return inventory; } @@ -49,4 +65,12 @@ public class FishingBagHolder implements InventoryHolder { public void setInventory(Inventory inventory) { this.inventory = inventory; } + + public static FishingBagHolder create(UUID owner, ItemStack[] itemStacks, int size) { + FishingBagHolder holder = new FishingBagHolder(owner); + Inventory inventory = Bukkit.createInventory(holder, size); + holder.setInventory(inventory); + holder.setItems(itemStacks); + return holder; + } } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockConfig.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockConfig.java index d7da2a56..8f601de7 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockConfig.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockConfig.java @@ -19,85 +19,80 @@ package net.momirealms.customfishing.api.mechanic.block; import java.util.List; -public class BlockConfig implements BlockSettings { +/** + * Interface representing the configuration for a custom block in the CustomFishing plugin. + * Provides methods to access various block properties and modifiers. + */ +public interface BlockConfig { - private String blockID; - private List dataModifierList; - private List stateModifierList; - private boolean persist; - private double horizontalVector; - private double verticalVector; + String id(); - @Override - public String getBlockID() { - return blockID; + /** + * Gets the unique identifier for the block. + * + * @return The block's unique identifier. + */ + String blockID(); + + /** + * Gets the list of data modifiers applied to the block. + * + * @return A list of {@link BlockDataModifier} objects. + */ + List dataModifier(); + + /** + * Gets the list of state modifiers applied to the block. + * + * @return A list of {@link BlockStateModifier} objects. + */ + List stateModifiers(); + + /** + * Creates a new builder instance for constructing a {@link BlockConfig}. + * + * @return A new {@link Builder} instance. + */ + static Builder builder() { + return new BlockConfigImpl.BuilderImpl(); } - @Override - public List getDataModifier() { - return dataModifierList; + /** + * Builder interface for constructing a {@link BlockConfig} instance. + */ + interface Builder { + + Builder id(String id); + + /** + * Sets the block ID for the configuration. + * + * @param blockID The block's unique identifier. + * @return The current {@link Builder} instance. + */ + Builder blockID(String blockID); + + /** + * Sets the list of data modifiers for the configuration. + * + * @param dataModifierList A list of {@link BlockDataModifier} objects. + * @return The current {@link Builder} instance. + */ + Builder dataModifierList(List dataModifierList); + + /** + * Sets the list of state modifiers for the configuration. + * + * @param stateModifierList A list of {@link BlockStateModifier} objects. + * @return The current {@link Builder} instance. + */ + Builder stateModifierList(List stateModifierList); + + /** + * Builds and returns the configured {@link BlockConfig} instance. + * + * @return The constructed {@link BlockConfig} instance. + */ + BlockConfig build(); } - - @Override - public List getStateModifierList() { - return stateModifierList; - } - - @Override - public boolean isPersist() { - return persist; - } - - @Override - public double getHorizontalVector() { - return horizontalVector; - } - - @Override - public double getVerticalVector() { - return verticalVector; - } - - public static class Builder { - - private final BlockConfig config; - - public Builder() { - this.config = new BlockConfig(); - } - - public Builder persist(boolean value) { - config.persist = value; - return this; - } - - public Builder horizontalVector(double value) { - config.horizontalVector = value; - return this; - } - - public Builder verticalVector(double value) { - config.verticalVector = value; - return this; - } - - public Builder blockID(String value) { - config.blockID = value; - return this; - } - - public Builder dataModifiers(List value) { - config.dataModifierList = value; - return this; - } - - public Builder stateModifiers(List value) { - config.stateModifierList = value; - return this; - } - - public BlockConfig build() { - return config; - } - } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockConfigImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockConfigImpl.java new file mode 100644 index 00000000..ffcd0879 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockConfigImpl.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.block; + +import java.util.ArrayList; +import java.util.List; + +public class BlockConfigImpl implements BlockConfig { + + private final String blockID; + private final List dataModifierList; + private final List stateModifierList; + private final String id; + + public BlockConfigImpl(String id, String blockID, List dataModifierList, List stateModifierList) { + this.blockID = blockID; + this.dataModifierList = dataModifierList; + this.stateModifierList = stateModifierList; + this.id = id; + } + + @Override + public String id() { + return id; + } + + @Override + public String blockID() { + return blockID; + } + + @Override + public List dataModifier() { + return dataModifierList; + } + + @Override + public List stateModifiers() { + return stateModifierList; + } + + public static class BuilderImpl implements Builder { + private String blockID; + private final List dataModifierList = new ArrayList<>(); + private final List stateModifierList = new ArrayList<>(); + private String id; + @Override + public Builder id(String id) { + this.id = id; + return this; + } + @Override + public Builder blockID(String blockID) { + this.blockID = blockID; + return this; + } + @Override + public Builder dataModifierList(List dataModifierList) { + this.dataModifierList.addAll(dataModifierList); + return this; + } + @Override + public Builder stateModifierList(List stateModifierList) { + this.stateModifierList.addAll(stateModifierList); + return this; + } + @Override + public BlockConfig build() { + return new BlockConfigImpl(id, blockID, dataModifierList, stateModifierList); + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockDataModifier.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockDataModifier.java index 6ba31318..6830a4e5 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockDataModifier.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockDataModifier.java @@ -17,9 +17,12 @@ package net.momirealms.customfishing.api.mechanic.block; +import net.momirealms.customfishing.api.mechanic.context.Context; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Player; +@FunctionalInterface public interface BlockDataModifier { - void apply(Player player, BlockData blockData); + + void apply(Context context, BlockData blockData); } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockStateModifierBuilder.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockDataModifierFactory.java similarity index 87% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockStateModifierBuilder.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockDataModifierFactory.java index 3503cd3f..a92bdbfc 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockStateModifierBuilder.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockDataModifierFactory.java @@ -17,7 +17,8 @@ package net.momirealms.customfishing.api.mechanic.block; -public interface BlockStateModifierBuilder { +@FunctionalInterface +public interface BlockDataModifierFactory { - BlockStateModifier build(Object args); + BlockDataModifier process(Object args); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockLibrary.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockManager.java similarity index 63% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockLibrary.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockManager.java index e530a6b9..d6faca80 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockLibrary.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockManager.java @@ -17,19 +17,20 @@ package net.momirealms.customfishing.api.mechanic.block; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; import org.bukkit.block.Block; -import org.bukkit.block.data.BlockData; +import org.bukkit.entity.FallingBlock; import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; -import java.util.List; +public interface BlockManager extends Reloadable { -public interface BlockLibrary { + boolean registerBlock(@NotNull BlockConfig block); - String identification(); + @NotNull + FallingBlock summonBlockLoot(@NotNull Context context); - BlockData getBlockData(Player player, String id, List modifiers); - - @Nullable - String getBlockID(Block block); + @NotNull + String getBlockID(@NotNull Block block); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockStateModifier.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockStateModifier.java index 60af5f23..d4007c31 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockStateModifier.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockStateModifier.java @@ -17,9 +17,12 @@ package net.momirealms.customfishing.api.mechanic.block; +import net.momirealms.customfishing.api.mechanic.context.Context; import org.bukkit.block.BlockState; import org.bukkit.entity.Player; +@FunctionalInterface public interface BlockStateModifier { - void apply(Player player, BlockState blockState); + + void apply(Context context, BlockState blockState); } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockDataModifierBuilder.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockStateModifierFactory.java similarity index 87% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockDataModifierBuilder.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockStateModifierFactory.java index da0e2e0b..9e95d858 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockDataModifierBuilder.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockStateModifierFactory.java @@ -17,7 +17,8 @@ package net.momirealms.customfishing.api.mechanic.block; -public interface BlockDataModifierBuilder { +@FunctionalInterface +public interface BlockStateModifierFactory { - BlockDataModifier build(Object args); + BlockStateModifier process(Object args); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/EmptyBlockDataModifier.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/EmptyBlockDataModifier.java new file mode 100644 index 00000000..b67bd45e --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/EmptyBlockDataModifier.java @@ -0,0 +1,14 @@ +package net.momirealms.customfishing.api.mechanic.block; + +import net.momirealms.customfishing.api.mechanic.context.Context; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; + +public class EmptyBlockDataModifier implements BlockDataModifier { + + public static final BlockDataModifier INSTANCE = new EmptyBlockDataModifier(); + + @Override + public void apply(Context context, BlockData blockData) { + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/EmptyBlockStateModifier.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/EmptyBlockStateModifier.java new file mode 100644 index 00000000..68558c7b --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/EmptyBlockStateModifier.java @@ -0,0 +1,14 @@ +package net.momirealms.customfishing.api.mechanic.block; + +import net.momirealms.customfishing.api.mechanic.context.Context; +import org.bukkit.block.BlockState; +import org.bukkit.entity.Player; + +public class EmptyBlockStateModifier implements BlockStateModifier { + + public static final EmptyBlockStateModifier INSTANCE = new EmptyBlockStateModifier(); + + @Override + public void apply(Context context, BlockState blockState) { + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/ActionBarConfig.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/ActionBarConfig.java deleted file mode 100644 index 06a39372..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/ActionBarConfig.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic.competition; - -public class ActionBarConfig extends AbstractCompetitionInfo { - - public static class Builder { - - private final ActionBarConfig config; - - public Builder() { - this.config = new ActionBarConfig(); - } - - public Builder showToAll(boolean showToAll) { - this.config.showToAll = showToAll; - return this; - } - - public Builder refreshRate(int rate) { - this.config.refreshRate = rate; - return this; - } - - public Builder switchInterval(int interval) { - this.config.switchInterval = interval; - return this; - } - - public Builder text(String[] texts) { - this.config.texts = texts; - return this; - } - - public ActionBarConfig build() { - return this.config; - } - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/BossBarConfig.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/BossBarConfig.java deleted file mode 100644 index 32458903..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/BossBarConfig.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic.competition; - -import org.bukkit.boss.BarColor; - -public class BossBarConfig extends AbstractCompetitionInfo { - - private BarColor color; - private Overlay overlay; - - public BarColor getColor() { - return color; - } - - public Overlay getOverlay() { - return overlay; - } - - public static class Builder { - - private final BossBarConfig config; - - public Builder() { - this.config = new BossBarConfig(); - } - - public Builder showToAll(boolean showToAll) { - this.config.showToAll = showToAll; - return this; - } - - public Builder refreshRate(int rate) { - this.config.refreshRate = rate; - return this; - } - - public Builder switchInterval(int interval) { - this.config.switchInterval = interval; - return this; - } - - public Builder text(String[] texts) { - this.config.texts = texts; - return this; - } - - public Builder color(BarColor color) { - this.config.color = color; - return this; - } - - public Builder overlay(Overlay overlay) { - this.config.overlay = overlay; - return this; - } - - public BossBarConfig build() { - return this.config; - } - } - - public enum Overlay { - NOTCHED_6, - NOTCHED_10, - NOTCHED_12, - NOTCHED_20, - PROGRESS - } -} 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 adc698a0..4b714582 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,178 +18,227 @@ package net.momirealms.customfishing.api.mechanic.competition; import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.competition.info.ActionBarConfig; +import net.momirealms.customfishing.api.mechanic.competition.info.BossBarConfig; import net.momirealms.customfishing.api.mechanic.requirement.Requirement; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.bukkit.entity.Player; import java.util.HashMap; -public class CompetitionConfig { +/** + * Interface representing the configuration for a fishing competition. + */ +public interface CompetitionConfig { - private final String key; - private int duration; - private int minPlayers; - private BossBarConfig bossBarConfig; - private ActionBarConfig actionBarConfig; - private Action[] skipActions; - private Action[] startActions; - private Action[] endActions; - private Action[] joinActions; - private Requirement[] requirements; - private CompetitionGoal goal; - private HashMap rewards; + CompetitionGoal DEFAULT_GOAL = CompetitionGoal.CATCH_AMOUNT; + int DEFAULT_DURATION = 300; + int DEFAULT_MIN_PLAYERS = 0; + Requirement[] DEFAULT_REQUIREMENTS = null; + Action[] DEFAULT_SKIP_ACTIONS = null; + Action[] DEFAULT_START_ACTIONS = null; + Action[] DEFAULT_END_ACTIONS = null; + Action[] DEFAULT_JOIN_ACTIONS = null; + HashMap[]> DEFAULT_REWARDS = new HashMap<>(); - public CompetitionConfig(String key) { - this.key = key; - } + /** + * Gets the unique key for the competition. + * + * @return the key for the competition. + */ + String key(); - public String getKey() { - return key; - } + /** + * Gets the duration of the competition in seconds. + * + * @return the duration in seconds. + */ + int durationInSeconds(); - public int getDurationInSeconds() { - return duration; - } + /** + * Gets the minimum number of players required to start the competition. + * + * @return the minimum number of players. + */ + int minPlayersToStart(); - public int getMinPlayersToStart() { - return minPlayers; - } + /** + * Gets the actions to be performed when the competition starts. + * + * @return an array of start actions. + */ + Action[] startActions(); - @Nullable - public Action[] getStartActions() { - return startActions; - } + /** + * Gets the actions to be performed when the competition ends. + * + * @return an array of end actions. + */ + Action[] endActions(); - @Nullable - public Action[] getEndActions() { - return endActions; + /** + * Gets the actions to be performed when a player joins the competition. + * + * @return an array of join actions. + */ + Action[] joinActions(); + + /** + * Gets the actions to be performed when a player skips the competition. + * + * @return an array of skip actions. + */ + Action[] skipActions(); + + /** + * Gets the requirements that players must meet to join the competition. + * + * @return an array of join requirements. + */ + Requirement[] joinRequirements(); + + /** + * Gets the goal of the competition. + * + * @return the competition goal. + */ + CompetitionGoal goal(); + + /** + * Gets the rewards for the competition. + * + * @return a hashmap where the key is a string identifier and the value is an array of actions. + */ + HashMap[]> rewards(); + + /** + * Gets the configuration for the boss bar during the competition. + * + * @return the boss bar configuration. + */ + BossBarConfig bossBarConfig(); + + /** + * Gets the configuration for the action bar during the competition. + * + * @return the action bar configuration. + */ + ActionBarConfig actionBarConfig(); + + /** + * Creates a new builder for the competition configuration. + * + * @return a new builder instance. + */ + static Builder builder() { + return new CompetitionConfigImpl.BuilderImpl(); } /** - * Get the actions to perform if player joined the competition - * - * @return actions + * Builder interface for constructing a CompetitionConfig instance. */ - @Nullable - public Action[] getJoinActions() { - return joinActions; - } + interface Builder { - /** - * Get the actions to perform if the amount of players doesn't meet the requirement - * - * @return actions - */ - @Nullable - public Action[] getSkipActions() { - return skipActions; - } + /** + * Sets the unique key for the competition. + * + * @param key the key for the competition. + * @return the builder instance. + */ + Builder key(String key); - /** - * Get the requirements for participating the competition - * - * @return requirements - */ - @Nullable - public Requirement[] getRequirements() { - return requirements; - } + /** + * Sets the goal of the competition. + * + * @param goal the competition goal. + * @return the builder instance. + */ + Builder goal(CompetitionGoal goal); - @NotNull - public CompetitionGoal getGoal() { - return goal; - } + /** + * Sets the duration of the competition. + * + * @param duration the duration in seconds. + * @return the builder instance. + */ + Builder duration(int duration); - /** - * Get the reward map - * - * @return reward map - */ - public HashMap getRewards() { - return rewards; - } + /** + * Sets the minimum number of players required to start the competition. + * + * @param minPlayers the minimum number of players. + * @return the builder instance. + */ + Builder minPlayers(int minPlayers); - @Nullable - public BossBarConfig getBossBarConfig() { - return bossBarConfig; - } + /** + * Sets the requirements that players must meet to join the competition. + * + * @param joinRequirements an array of join requirements. + * @return the builder instance. + */ + Builder joinRequirements(Requirement[] joinRequirements); - @Nullable - public ActionBarConfig getActionBarConfig() { - return actionBarConfig; - } + /** + * Sets the actions to be performed when a player skips the competition. + * + * @param skipActions an array of skip actions. + * @return the builder instance. + */ + Builder skipActions(Action[] skipActions); - public static Builder builder(String key) { - return new Builder(key); - } + /** + * Sets the actions to be performed when the competition starts. + * + * @param startActions an array of start actions. + * @return the builder instance. + */ + Builder startActions(Action[] startActions); - public static class Builder { + /** + * Sets the actions to be performed when the competition ends. + * + * @param endActions an array of end actions. + * @return the builder instance. + */ + Builder endActions(Action[] endActions); - private final CompetitionConfig config; + /** + * Sets the actions to be performed when a player joins the competition. + * + * @param joinActions an array of join actions. + * @return the builder instance. + */ + Builder joinActions(Action[] joinActions); - public Builder(String key) { - this.config = new CompetitionConfig(key); - } + /** + * Sets the rewards for the competition. + * + * @param rewards a hashmap where the key is a string identifier and the value is an array of actions. + * @return the builder instance. + */ + Builder rewards(HashMap[]> rewards); - public Builder duration(int duration) { - config.duration = duration; - return this; - } + /** + * Sets the configuration for the boss bar during the competition. + * + * @param bossBarConfig the boss bar configuration. + * @return the builder instance. + */ + Builder bossBarConfig(BossBarConfig bossBarConfig); - public Builder minPlayers(int min) { - config.minPlayers = min; - return this; - } + /** + * Sets the configuration for the action bar during the competition. + * + * @param actionBarConfig the action bar configuration. + * @return the builder instance. + */ + Builder actionBarConfig(ActionBarConfig actionBarConfig); - public Builder startActions(Action[] startActions) { - config.startActions = startActions; - return this; - } - - public Builder endActions(Action[] endActions) { - config.endActions = endActions; - return this; - } - - public Builder skipActions(Action[] skipActions) { - config.skipActions = skipActions; - return this; - } - - public Builder joinActions(Action[] joinActions) { - config.joinActions = joinActions; - 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; - } - - public Builder requirements(Requirement[] requirements) { - config.requirements = requirements; - return this; - } - - public Builder goal(CompetitionGoal goal) { - config.goal = goal; - return this; - } - - public Builder rewards(HashMap rewards) { - config.rewards = rewards; - return this; - } - - public CompetitionConfig build() { - return config; - } + /** + * Builds and returns the CompetitionConfig instance. + * + * @return the constructed CompetitionConfig instance. + */ + CompetitionConfig build(); } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionConfigImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionConfigImpl.java new file mode 100644 index 00000000..14bad1e3 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionConfigImpl.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.competition; + +import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.competition.info.ActionBarConfig; +import net.momirealms.customfishing.api.mechanic.competition.info.BossBarConfig; +import net.momirealms.customfishing.api.mechanic.requirement.Requirement; +import org.bukkit.entity.Player; + +import java.util.HashMap; + +public class CompetitionConfigImpl implements CompetitionConfig { + + private final String key; + private final CompetitionGoal goal; + private final int duration; + private final int minPlayers; + private final Requirement[] joinRequirements; + private final Action[] skipActions; + private final Action[] startActions; + private final Action[] endActions; + private final Action[] joinActions; + private final HashMap[]> rewards; + private final BossBarConfig bossBarConfig; + private final ActionBarConfig actionBarConfig; + + public CompetitionConfigImpl(String key, CompetitionGoal goal, int duration, int minPlayers, Requirement[] joinRequirements, Action[] skipActions, Action[] startActions, Action[] endActions, Action[] joinActions, HashMap[]> rewards, BossBarConfig bossBarConfig, ActionBarConfig actionBarConfig) { + this.key = key; + this.goal = goal; + this.duration = duration; + this.minPlayers = minPlayers; + this.joinRequirements = joinRequirements; + this.skipActions = skipActions; + this.startActions = startActions; + this.endActions = endActions; + this.joinActions = joinActions; + this.rewards = rewards; + this.bossBarConfig = bossBarConfig; + this.actionBarConfig = actionBarConfig; + } + + @Override + public String key() { + return key; + } + + @Override + public int durationInSeconds() { + return duration; + } + + @Override + public int minPlayersToStart() { + return minPlayers; + } + + @Override + public Action[] startActions() { + return startActions; + } + + @Override + public Action[] endActions() { + return endActions; + } + + @Override + public Action[] joinActions() { + return joinActions; + } + + @Override + public Action[] skipActions() { + return skipActions; + } + + @Override + public Requirement[] joinRequirements() { + return joinRequirements; + } + + @Override + public CompetitionGoal goal() { + return goal; + } + + @Override + public HashMap[]> rewards() { + return rewards; + } + + @Override + public BossBarConfig bossBarConfig() { + return bossBarConfig; + } + + @Override + public ActionBarConfig actionBarConfig() { + return actionBarConfig; + } + + public static class BuilderImpl implements Builder { + private String key; + private CompetitionGoal goal = DEFAULT_GOAL; + private int duration = DEFAULT_DURATION; + private int minPlayers = DEFAULT_MIN_PLAYERS; + private Requirement[] joinRequirements = DEFAULT_REQUIREMENTS; + private Action[] skipActions = DEFAULT_SKIP_ACTIONS; + private Action[] startActions = DEFAULT_START_ACTIONS; + private Action[] endActions = DEFAULT_END_ACTIONS; + private Action[] joinActions = DEFAULT_JOIN_ACTIONS; + private HashMap[]> rewards = DEFAULT_REWARDS; + private BossBarConfig bossBarConfig; + private ActionBarConfig actionBarConfig; + @Override + public Builder key(String key) { + this.key = key; + return this; + } + @Override + public Builder goal(CompetitionGoal goal) { + this.goal = goal; + return this; + } + @Override + public Builder duration(int duration) { + this.duration = duration; + return this; + } + @Override + public Builder minPlayers(int minPlayers) { + this.minPlayers = minPlayers; + return this; + } + @Override + public Builder joinRequirements(Requirement[] joinRequirements) { + this.joinRequirements = joinRequirements; + return this; + } + @Override + public Builder skipActions(Action[] skipActions) { + this.skipActions = skipActions; + return this; + } + @Override + public Builder startActions(Action[] startActions) { + this.startActions = startActions; + return this; + } + @Override + public Builder endActions(Action[] endActions) { + this.endActions = endActions; + return this; + } + @Override + public Builder joinActions(Action[] joinActions) { + this.joinActions = joinActions; + return this; + } + @Override + public Builder rewards(HashMap[]> rewards) { + this.rewards = rewards; + return this; + } + @Override + public Builder bossBarConfig(BossBarConfig bossBarConfig) { + this.bossBarConfig = bossBarConfig; + return this; + } + @Override + public Builder actionBarConfig(ActionBarConfig actionBarConfig) { + this.actionBarConfig = actionBarConfig; + return this; + } + @Override + public CompetitionConfig build() { + return new CompetitionConfigImpl(key, goal, duration, minPlayers, joinRequirements, skipActions, startActions, endActions, joinActions, rewards, bossBarConfig, actionBarConfig); + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionGoal.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionGoal.java index 306471a9..7560c7c4 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionGoal.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionGoal.java @@ -17,17 +17,115 @@ package net.momirealms.customfishing.api.mechanic.competition; -import java.util.concurrent.ThreadLocalRandom; +import net.kyori.adventure.util.Index; +import net.momirealms.customfishing.common.locale.MessageConstants; +import net.momirealms.customfishing.common.locale.TranslationManager; +import net.momirealms.customfishing.common.util.RandomUtils; +import org.apache.logging.log4j.util.Supplier; +import org.apache.logging.log4j.util.TriConsumer; +import org.bukkit.entity.Player; -public enum CompetitionGoal { +import java.util.Optional; - CATCH_AMOUNT, - TOTAL_SCORE, - MAX_SIZE, - TOTAL_SIZE, - RANDOM; +public final class CompetitionGoal { + public static final CompetitionGoal CATCH_AMOUNT = new CompetitionGoal( + "catch_amount", + ((rankingProvider, player, score) -> rankingProvider.refreshData(player, 1)), + () -> Optional.ofNullable(TranslationManager.miniMessageTranslation(MessageConstants.GOAL_CATCH_AMOUNT.build().key())).orElse("catch_amount") + ); + public static final CompetitionGoal TOTAL_SCORE = new CompetitionGoal( + "total_score", + (RankingProvider::refreshData), + () -> Optional.ofNullable(TranslationManager.miniMessageTranslation(MessageConstants.GOAL_TOTAL_SCORE.build().key())).orElse("total_score") + ); + public static final CompetitionGoal MAX_SIZE = new CompetitionGoal( + "max_size", + ((rankingProvider, player, score) -> { + if (rankingProvider.getPlayerScore(player) < score) { + rankingProvider.setData(player, score); + } + }), + () -> Optional.ofNullable(TranslationManager.miniMessageTranslation(MessageConstants.GOAL_MAX_SIZE.build().key())).orElse("max_size") + ); + public static final CompetitionGoal MIN_SIZE = new CompetitionGoal( + "min_size", + ((rankingProvider, player, score) -> { + if (rankingProvider.getPlayerScore(player) > score) { + rankingProvider.setData(player, score); + } + }), + () -> Optional.ofNullable(TranslationManager.miniMessageTranslation(MessageConstants.GOAL_MIN_SIZE.build().key())).orElse("min_size") + ); + public static final CompetitionGoal TOTAL_SIZE = new CompetitionGoal( + "total_size", + (RankingProvider::refreshData), + () -> Optional.ofNullable(TranslationManager.miniMessageTranslation(MessageConstants.GOAL_TOTAL_SIZE.build().key())).orElse("total_size") + ); + public static final CompetitionGoal RANDOM = new CompetitionGoal( + "random", + (rankingProvider, player, score) -> {}, + () -> "random" + ); + + private static final CompetitionGoal[] values = new CompetitionGoal[] { + CATCH_AMOUNT, TOTAL_SCORE, MAX_SIZE, TOTAL_SIZE, RANDOM + }; + + private static final Index index = Index.create(CompetitionGoal::key, values()); + + /** + * Gets an array containing all defined competition goals. + * + * @return An array of all competition goals. + */ + public static CompetitionGoal[] values() { + return values; + } + + /** + * Gets the index of competition goals by their keys. + * + * @return An index mapping keys to competition goals. + */ + public static Index index() { + return index; + } + + /** + * Gets a randomly selected competition goal. + * + * @return A randomly selected competition goal. + */ public static CompetitionGoal getRandom() { - return CompetitionGoal.values()[ThreadLocalRandom.current().nextInt(CompetitionGoal.values().length - 1)]; + return CompetitionGoal.values()[RandomUtils.generateRandomInt(0, values.length - 1)]; + } + + private final String key; + private final TriConsumer scoreConsumer; + private final Supplier nameSupplier; + + private CompetitionGoal(String key, TriConsumer scoreConsumer, Supplier nameSupplier) { + this.key = key; + this.scoreConsumer = scoreConsumer; + this.nameSupplier = nameSupplier; + } + + /** + * Gets the key representing this competition goal. + * + * @return The key of the competition goal. + */ + public String key() { + return key; + } + + public void refreshScore(RankingProvider ranking, Player player, Double score) { + scoreConsumer.accept(ranking, player.getName(), score); + } + + @Override + public String toString() { + return nameSupplier.get(); } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionManager.java new file mode 100644 index 00000000..91efc706 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionManager.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.competition; + +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +public interface CompetitionManager extends Reloadable { + + boolean startCompetition(String competition, boolean force, @Nullable String serverGroup); + + boolean startCompetition(CompetitionConfig config, boolean force, @Nullable String serverGroup); + + @Nullable + FishingCompetition getOnGoingCompetition(); + + int getNextCompetitionInSeconds(); + + @Nullable + CompetitionConfig getCompetition(String key); + + Collection getCompetitionIDs(); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionPlayer.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionPlayer.java index 5a18ef9e..7ae2597c 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionPlayer.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/CompetitionPlayer.java @@ -19,52 +19,98 @@ package net.momirealms.customfishing.api.mechanic.competition; import org.jetbrains.annotations.NotNull; -public class CompetitionPlayer implements Comparable{ +/** + * Represents a player participating in a fishing competition. + */ +public class CompetitionPlayer implements Comparable { - public static CompetitionPlayer empty = new CompetitionPlayer("", 0); - private long time; private final String player; + private long time; private double score; + /** + * Constructs a new CompetitionPlayer with the specified player name and initial score. + * + * @param player the name of the player. + * @param score the initial score of the player. + */ public CompetitionPlayer(String player, double score) { this.player = player; this.score = score; this.time = System.currentTimeMillis(); } - public void addScore(double score){ + /** + * Adds the specified score to the player's current score. + * If the added score is positive, updates the player's time to the current time. + * + * @param score the score to add. + */ + public void addScore(double score) { this.score += score; + if (score <= 0) return; this.time = System.currentTimeMillis(); } - public void setScore(double score){ + /** + * Sets the player's score to the specified value and updates the player's time to the current time. + * + * @param score the new score for the player. + */ + public void setScore(double score) { this.score = score; this.time = System.currentTimeMillis(); } + /** + * Gets the time when the player's score was last updated. + * + * @return the last update time in milliseconds. + */ public long getTime() { - return time; + return this.time; } + /** + * Gets the player's current score. + * + * @return the current score. + */ public double getScore() { return this.score; } - public String getPlayer(){ + /** + * Gets the name of the player. + * + * @return the player's name. + */ + public String getPlayer() { return this.player; } + /** + * Compares this player to another CompetitionPlayer for ordering. + * Players are compared first by score, then by time if scores are equal. + * + * @param another the other player to compare to. + */ @Override - public int compareTo(@NotNull CompetitionPlayer competitionPlayer) { - if (competitionPlayer.getScore() != this.score) { - return (competitionPlayer.getScore() > this.score) ? 1 : -1; - } else if (competitionPlayer.getTime() != this.time) { - return (competitionPlayer.getTime() > this.time) ? 1 : -1; + public int compareTo(@NotNull CompetitionPlayer another) { + if (another.getScore() != this.score) { + return (another.getScore() > this.score) ? 1 : -1; + } else if (another.getTime() != this.time) { + return (another.getTime() > this.time) ? 1 : -1; } else { return 0; } } + /** + * Returns a string representation of the CompetitionPlayer. + * + * @return a string containing the player's name, score, and last update time. + */ @Override public String toString() { return "CompetitionPlayer[" + 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 1e5d7b1d..f19b46bd 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 @@ -17,19 +17,17 @@ package net.momirealms.customfishing.api.mechanic.competition; +import net.momirealms.customfishing.api.mechanic.context.Context; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; public interface FishingCompetition { /** * Start the fishing competition */ - void start(); + void start(boolean triggerEvent); /** * Stop the fishing competition @@ -91,34 +89,29 @@ public interface FishingCompetition { * * @return The configuration of the fishing competition. */ - @NotNull CompetitionConfig getConfig(); + @NotNull + CompetitionConfig getConfig(); /** * Gets the goal of the fishing competition. * * @return The goal of the fishing competition. */ - @NotNull CompetitionGoal getGoal(); + @NotNull + CompetitionGoal getGoal(); /** * Gets the ranking data for the fishing competition. * * @return The ranking data for the fishing competition. */ - @NotNull Ranking getRanking(); + @NotNull + RankingProvider getRanking(); /** - * Gets the cached placeholders for the fishing competition. + * Get the public context * - * @return A ConcurrentHashMap containing cached placeholders. + * @return public context */ - @NotNull Map getCachedPlaceholders(); - - /** - * 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); + Context getPublicContext(); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/Ranking.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/RankingProvider.java similarity index 94% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/Ranking.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/RankingProvider.java index 728ac725..7a65432e 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/Ranking.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/RankingProvider.java @@ -17,12 +17,12 @@ package net.momirealms.customfishing.api.mechanic.competition; -import net.momirealms.customfishing.api.common.Pair; +import net.momirealms.customfishing.common.util.Pair; import org.jetbrains.annotations.Nullable; import java.util.Iterator; -public interface Ranking { +public interface RankingProvider { /** * Clears the list of competition players. @@ -62,9 +62,9 @@ public interface Ranking { void removePlayer(String player); /** - * Returns an iterator for iterating over pairs of player names and scores. + * Returns an iterator for iterating over items of player names and scores. * - * @return An iterator for pairs of player names and scores. + * @return An iterator for items of player names and scores. */ Iterator> getIterator(); 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/info/AbstractCompetitionInfo.java similarity index 71% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/AbstractCompetitionInfo.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/AbstractCompetitionInfo.java index 434395d7..d241e1ac 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/info/AbstractCompetitionInfo.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.mechanic.competition; +package net.momirealms.customfishing.api.mechanic.competition.info; /** * Abstract base class for competition information. @@ -27,13 +27,22 @@ public abstract class AbstractCompetitionInfo { protected int switchInterval; protected boolean showToAll; protected String[] texts; + protected boolean enabled; + + protected AbstractCompetitionInfo(boolean enabled, int refreshRate, int switchInterval, boolean showToAll, String[] texts) { + this.refreshRate = refreshRate; + this.switchInterval = switchInterval; + this.showToAll = showToAll; + this.texts = texts; + this.enabled = enabled; + } /** * Get the refresh rate for updating competition information. * * @return The refresh rate in ticks. */ - public int getRefreshRate() { + public int refreshRate() { return refreshRate; } @@ -42,7 +51,7 @@ public abstract class AbstractCompetitionInfo { * * @return The switch interval in ticks. */ - public int getSwitchInterval() { + public int switchInterval() { return switchInterval; } @@ -51,7 +60,7 @@ public abstract class AbstractCompetitionInfo { * * @return True if information is shown to all players, otherwise only to participants. */ - public boolean isShowToAll() { + public boolean showToAll() { return showToAll; } @@ -60,7 +69,16 @@ public abstract class AbstractCompetitionInfo { * * @return An array of competition information texts. */ - public String[] getTexts() { + public String[] texts() { return texts; } + + /** + * If the feature is enabled. + * + * @return enabled or not. + */ + public boolean enabled() { + return enabled; + } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/ActionBarConfig.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/ActionBarConfig.java new file mode 100644 index 00000000..08cf314a --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/ActionBarConfig.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.competition.info; + +public interface ActionBarConfig { + + int DEFAULT_REFRESH_RATE = 20; + int DEFAULT_SWITCH_INTERVAL = 200; + boolean DEFAULT_VISIBILITY = true; + String[] DEFAULT_TEXTS = new String[]{""}; + + /** + * Get the refresh rate for updating the competition information on the action bar. + * + * @return The refresh rate in ticks. + */ + int refreshRate(); + + /** + * Get the switch interval for displaying different competition texts. + * + * @return The switch interval in ticks. + */ + int switchInterval(); + + /** + * Check if competition information should be shown to all players. + * + * @return True if information is shown to all players, otherwise only to participants. + */ + boolean showToAll(); + + /** + * Get an array of competition information texts. + * + * @return An array of competition information texts. + */ + String[] texts(); + + /** + * Is action bar enabled + * + * @return enabled or not + */ + boolean enabled(); + + /** + * Creates a new builder instance for constructing {@code ActionBarConfig} objects. + * + * @return A new {@code Builder} instance. + */ + static Builder builder() { + return new ActionBarConfigImpl.BuilderImpl(); + } + + /** + * Builder interface for constructing {@code ActionBarConfig} objects. + */ + interface Builder { + + /** + * Sets whether the competition information should be shown to all players. + * + * @param showToAll True to show information to all players, false to show only to participants. + * @return The current {@code Builder} instance. + */ + Builder showToAll(boolean showToAll); + + /** + * Sets the refresh rate for updating the competition information. + * + * @param rate The refresh rate in ticks. + * @return The current {@code Builder} instance. + */ + Builder refreshRate(int rate); + + /** + * Sets the interval for switching between different competition texts. + * + * @param interval The switch interval in ticks. + * @return The current {@code Builder} instance. + */ + Builder switchInterval(int interval); + + /** + * Sets the texts to be displayed on the action bar during the competition. + * + * @param texts An array of competition information texts. + * @return The current {@code Builder} instance. + */ + Builder text(String[] texts); + + Builder enable(boolean enable); + + /** + * Builds the {@code ActionBarConfig} object with the configured settings. + * + * @return The constructed {@code ActionBarConfig} object. + */ + ActionBarConfig build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/ActionBarConfigImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/ActionBarConfigImpl.java new file mode 100644 index 00000000..b0a359b4 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/ActionBarConfigImpl.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.competition.info; + +public class ActionBarConfigImpl extends AbstractCompetitionInfo implements ActionBarConfig { + + public ActionBarConfigImpl(boolean enable, int refreshRate, int switchInterval, boolean showToAll, String[] texts) { + super(enable, refreshRate, switchInterval, showToAll, texts); + } + + public static class BuilderImpl implements Builder { + private int refreshRate = DEFAULT_REFRESH_RATE; + private int switchInterval = DEFAULT_SWITCH_INTERVAL; + private boolean showToAll = DEFAULT_VISIBILITY; + private String[] texts = DEFAULT_TEXTS; + private boolean enable = true; + @Override + public Builder showToAll(boolean showToAll) { + this.showToAll = showToAll; + return this; + } + @Override + public Builder refreshRate(int rate) { + this.refreshRate = rate; + return this; + } + @Override + public Builder switchInterval(int interval) { + this.switchInterval = interval; + return this; + } + @Override + public Builder text(String[] texts) { + this.texts = texts; + return this; + } + @Override + public Builder enable(boolean enable) { + this.enable = enable; + return this; + } + @Override + public ActionBarConfig build() { + return new ActionBarConfigImpl(enable, refreshRate, switchInterval, showToAll, texts); + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/BossBarConfig.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/BossBarConfig.java new file mode 100644 index 00000000..f1144d27 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/BossBarConfig.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.competition.info; + +import net.kyori.adventure.bossbar.BossBar; + +public interface BossBarConfig { + + int DEFAULT_REFRESH_RATE = 20; + int DEFAULT_SWITCH_INTERVAL = 200; + boolean DEFAULT_VISIBILITY = true; + String[] DEFAULT_TEXTS = new String[]{""}; + BossBar.Color DEFAULT_COLOR = BossBar.Color.BLUE; + BossBar.Overlay DEFAULT_OVERLAY = BossBar.Overlay.PROGRESS; + + /** + * Get the refresh rate for updating competition information. + * + * @return The refresh rate in ticks. + */ + int refreshRate(); + + /** + * Get the switch interval for displaying different competition texts. + * + * @return The switch interval in ticks. + */ + int switchInterval(); + + /** + * Check if competition information should be shown to all players. + * + * @return True if information is shown to all players, otherwise only to participants. + */ + boolean showToAll(); + + /** + * Get an array of competition information texts. + * + * @return An array of competition information texts. + */ + String[] texts(); + + /** + * Gets the color of the boss bar. + * + * @return The color of the boss bar. + */ + BossBar.Color color(); + + /** + * Gets the overlay style of the boss bar. + * + * @return The overlay style of the boss bar. + */ + BossBar.Overlay overlay(); + + /** + * Is boss bar enabled + * + * @return enabled or not + */ + boolean enabled(); + + /** + * Creates a new builder instance for constructing {@code BossBarConfig} objects. + * + * @return A new {@code Builder} instance. + */ + static Builder builder() { + return new BossBarConfigImpl.BuilderImpl(); + } + + /** + * Builder interface for constructing {@code BossBarConfig} objects. + */ + interface Builder { + + /** + * Sets whether the competition information should be shown to all players. + * + * @param showToAll True to show information to all players, false to show only to participants. + * @return The current {@code Builder} instance. + */ + Builder showToAll(boolean showToAll); + + /** + * Sets the refresh rate for updating the competition information. + * + * @param rate The refresh rate in ticks. + * @return The current {@code Builder} instance. + */ + Builder refreshRate(int rate); + + /** + * Sets the interval for switching between different competition texts. + * + * @param interval The switch interval in ticks. + * @return The current {@code Builder} instance. + */ + Builder switchInterval(int interval); + + /** + * Sets the texts to be displayed on the boss bar during the competition. + * + * @param texts An array of competition information texts. + * @return The current {@code Builder} instance. + */ + Builder text(String[] texts); + + /** + * Sets the color of the boss bar. + * + * @param color The color of the boss bar. + * @return The current {@code Builder} instance. + */ + Builder color(BossBar.Color color); + + /** + * Sets the overlay style of the boss bar. + * + * @param overlay The overlay style of the boss bar. + * @return The current {@code Builder} instance. + */ + Builder overlay(BossBar.Overlay overlay); + + Builder enable(boolean enable); + + /** + * Builds the {@code BossBarConfig} object with the configured settings. + * + * @return The constructed {@code BossBarConfig} object. + */ + BossBarConfig build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/BossBarConfigImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/BossBarConfigImpl.java new file mode 100644 index 00000000..f2a50971 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/competition/info/BossBarConfigImpl.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.competition.info; + +import net.kyori.adventure.bossbar.BossBar; + +public class BossBarConfigImpl extends AbstractCompetitionInfo implements BossBarConfig { + + private final BossBar.Color color; + private final BossBar.Overlay overlay; + + public BossBarConfigImpl(boolean enable, int refreshRate, int switchInterval, boolean showToAll, String[] texts, BossBar.Color color, BossBar.Overlay overlay) { + super(enable, refreshRate, switchInterval, showToAll, texts); + this.color = color; + this.overlay = overlay; + } + + @Override + public BossBar.Color color() { + return color; + } + + @Override + public BossBar.Overlay overlay() { + return overlay; + } + + public static class BuilderImpl implements Builder { + private int refreshRate = DEFAULT_REFRESH_RATE; + private int switchInterval = DEFAULT_SWITCH_INTERVAL; + private boolean showToAll = DEFAULT_VISIBILITY; + private String[] texts = DEFAULT_TEXTS; + private BossBar.Overlay overlay = DEFAULT_OVERLAY; + private BossBar.Color color = DEFAULT_COLOR; + private boolean enable = true; + @Override + public Builder showToAll(boolean showToAll) { + this.showToAll = showToAll; + return this; + } + @Override + public Builder refreshRate(int rate) { + this.refreshRate = rate; + return this; + } + @Override + public Builder switchInterval(int interval) { + this.switchInterval = interval; + return this; + } + @Override + public Builder text(String[] texts) { + this.texts = texts; + return this; + } + @Override + public Builder color(BossBar.Color color) { + this.color = color; + return this; + } + @Override + public Builder overlay(BossBar.Overlay overlay) { + this.overlay = overlay; + return this; + } + @Override + public Builder enable(boolean enable) { + this.enable = enable; + return this; + } + @Override + public BossBarConfig build() { + return new BossBarConfigImpl(enable, refreshRate, switchInterval, showToAll, texts, color, overlay); + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/condition/Condition.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/condition/Condition.java deleted file mode 100644 index 8d41e070..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/condition/Condition.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic.condition; - -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents a condition with associated data - */ -public class Condition { - - protected Location location; - protected final Player player; - protected final @NotNull Map args; - - /** - * Creates a new Condition object based on a player's location. - * - * @param player The player associated with this condition. - */ - public Condition(@NotNull Player player) { - this(player.getLocation(), player, new HashMap<>()); - } - - /** - * Creates a new Condition object with specified arguments. - * - * @param player The player associated with this condition. - * @param args A map of arguments associated with this condition. - */ - public Condition(@NotNull Player player, @NotNull Map args) { - this(player.getLocation(), player, args); - } - - /** - * Creates a new Condition object with a specific location, player, and arguments. - * - * @param location The location associated with this condition. - * @param player The player associated with this condition. - * @param args A map of arguments associated with this condition. - */ - public Condition(Location location, Player player, @NotNull Map args) { - this.location = location; - this.player = player; - this.args = args; - if (player != null) - this.args.put("{player}", player.getName()); - if (location != null) { - this.args.put("{x}", String.valueOf(location.getX())); - this.args.put("{y}", String.valueOf(location.getY())); - this.args.put("{z}", String.valueOf(location.getZ())); - this.args.put("{world}", location.getWorld().getName()); - } - } - - /** - * Sets the location associated with this condition. - * - * @param location The new location to set. - */ - public void setLocation(@NotNull Location location) { - this.location = location; - this.args.put("{x}", String.valueOf(location.getX())); - this.args.put("{y}", String.valueOf(location.getY())); - this.args.put("{z}", String.valueOf(location.getZ())); - this.args.put("{world}", location.getWorld().getName()); - } - - /** - * Gets the location associated with this condition. - * - * @return The location associated with this condition. - */ - public Location getLocation() { - return location; - } - - /** - * Gets the player associated with this condition. - * - * @return The player associated with this condition. - */ - public Player getPlayer() { - return player; - } - - /** - * Gets the map of arguments associated with this condition. - * - * @return A map of arguments associated with this condition. - */ - @NotNull - public Map getArgs() { - return args; - } - - /** - * Gets the value of a specific argument by its key. - * - * @param key The key of the argument to retrieve. - * @return The value of the argument or null if not found. - */ - @Nullable - public String getArg(String key) { - return args.get(key); - } - - /** - * Inserts or updates an argument with the specified key and value. - * - * @param key The key of the argument to insert or update. - * @param value The value to set for the argument. - */ - public void insertArg(String key, String value) { - args.put(key, value); - } - - /** - * Deletes an argument with the specified key. - * - * @param key The key of the argument to delete. - * @return The value of the deleted argument or null if not found. - */ - public String delArg(String key) { - return args.remove(key); - } -} 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 deleted file mode 100644 index 2ce5b509..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/condition/FishingPreparation.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic.condition; - -import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; -import net.momirealms.customfishing.api.mechanic.effect.FishingEffect; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public abstract class FishingPreparation extends Condition { - - public FishingPreparation(Player player) { - super(player); - } - - /** - * Retrieves the ItemStack representing the fishing rod. - * - * @return The ItemStack representing the fishing rod. - */ - @NotNull - public abstract ItemStack getRodItemStack(); - - /** - * Retrieves the ItemStack representing the bait (if available). - * - * @return The ItemStack representing the bait, or null if no bait is set. - */ - @Nullable - public abstract ItemStack getBaitItemStack(); - - /** - * Checks if player meet the requirements for fishing gears - * - * @return True if can fish, false otherwise. - */ - public abstract boolean canFish(); - - /** - * Merges a FishingEffect into this fishing rod, applying effect modifiers. - * - * @param effect The FishingEffect to merge into this rod. - */ - public abstract void mergeEffect(FishingEffect effect); - - /** - * Triggers actions associated with a specific action trigger. - * - * @param actionTrigger The action trigger that initiates the actions. - */ - public abstract void triggerActions(ActionTrigger actionTrigger); -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/BaitConfigParser.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/BaitConfigParser.java new file mode 100644 index 00000000..2014af75 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/BaitConfigParser.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.config.function.*; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; +import net.momirealms.customfishing.api.mechanic.effect.LootBaseEffect; +import net.momirealms.customfishing.api.mechanic.event.EventCarrier; +import net.momirealms.customfishing.api.mechanic.item.CustomFishingItem; +import net.momirealms.customfishing.api.mechanic.loot.Loot; +import net.momirealms.customfishing.api.mechanic.loot.LootType; +import net.momirealms.customfishing.common.config.node.Node; +import net.momirealms.customfishing.common.item.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class BaitConfigParser { + + private final String id; + private final String material; + private final List, Context>>> tagConsumers = new ArrayList<>(); + private final List> eventBuilderConsumers = new ArrayList<>(); + private final List> effectBuilderConsumers = new ArrayList<>(); + private final List> baseEffectBuilderConsumers = new ArrayList<>(); + private final List> lootBuilderConsumers = new ArrayList<>(); + + public BaitConfigParser(String id, Section section, Map> functionMap) { + this.id = id; + this.material = section.getString("material"); + if (!section.contains("tag")) section.set("tag", true); + analyze(section, functionMap); + } + + private void analyze(Section section, Map> functionMap) { + Map dataMap = section.getStringRouteMappedValues(false); + for (Map.Entry entry : dataMap.entrySet()) { + String key = entry.getKey(); + Node node = functionMap.get(key); + if (node == null) continue; + ConfigParserFunction function = node.nodeValue(); + if (function != null) { + switch (function.type()) { + case BASE_EFFECT -> { + BaseEffectParserFunction baseEffectParserFunction = (BaseEffectParserFunction) function; + Consumer consumer = baseEffectParserFunction.accept(entry.getValue()); + baseEffectBuilderConsumers.add(consumer); + } + case LOOT -> { + LootParserFunction lootParserFunction = (LootParserFunction) function; + Consumer consumer = lootParserFunction.accept(entry.getValue()); + lootBuilderConsumers.add(consumer); + } + case ITEM -> { + ItemParserFunction propertyFunction = (ItemParserFunction) function; + BiConsumer, Context> result = propertyFunction.accept(entry.getValue()); + tagConsumers.add(new PriorityFunction<>(propertyFunction.getPriority(), result)); + } + case EVENT -> { + EventParserFunction eventParserFunction = (EventParserFunction) function; + Consumer consumer = eventParserFunction.accept(entry.getValue()); + eventBuilderConsumers.add(consumer); + } + case EFFECT_MODIFIER -> { + EffectModifierParserFunction effectModifierParserFunction = (EffectModifierParserFunction) function; + Consumer consumer = effectModifierParserFunction.accept(entry.getValue()); + effectBuilderConsumers.add(consumer); + } + } + continue; + } + if (entry.getValue() instanceof Section innerSection) { + analyze(innerSection, node.getChildTree()); + } + } + } + + public CustomFishingItem getItem() { + return CustomFishingItem.builder() + .material(material) + .id(id) + .tagConsumers(tagConsumers) + .build(); + } + + public EventCarrier getEventCarrier() { + EventCarrier.Builder builder = EventCarrier.builder() + .id(id) + .type(MechanicType.BAIT); + for (Consumer consumer : eventBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public EffectModifier getEffectModifier() { + EffectModifier.Builder builder = EffectModifier.builder() + .id(id) + .type(MechanicType.BAIT); + for (Consumer consumer : effectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + private LootBaseEffect getBaseEffect() { + LootBaseEffect.Builder builder = LootBaseEffect.builder(); + for (Consumer consumer : baseEffectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public Loot getLoot() { + Loot.Builder builder = Loot.builder() + .id(id) + .type(LootType.ITEM) + .lootBaseEffect(getBaseEffect()); + for (Consumer consumer : lootBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/BlockConfigParser.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/BlockConfigParser.java new file mode 100644 index 00000000..261f00a5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/BlockConfigParser.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.block.BlockConfig; +import net.momirealms.customfishing.api.mechanic.config.function.*; +import net.momirealms.customfishing.api.mechanic.effect.LootBaseEffect; +import net.momirealms.customfishing.api.mechanic.event.EventCarrier; +import net.momirealms.customfishing.api.mechanic.loot.Loot; +import net.momirealms.customfishing.api.mechanic.loot.LootType; +import net.momirealms.customfishing.common.config.node.Node; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class BlockConfigParser { + + private final String id; + private final List> blockBuilderConsumers = new ArrayList<>(); + private final List> effectBuilderConsumers = new ArrayList<>(); + private final List> lootBuilderConsumers = new ArrayList<>(); + private final List> eventBuilderConsumers = new ArrayList<>(); + + public BlockConfigParser(String id, Section section, Map> functionMap) { + this.id = id; + analyze(section, functionMap); + } + + private void analyze(Section section, Map> functionMap) { + Map dataMap = section.getStringRouteMappedValues(false); + for (Map.Entry entry : dataMap.entrySet()) { + String key = entry.getKey(); + Node node = functionMap.get(key); + if (node == null) continue; + ConfigParserFunction function = node.nodeValue(); + if (function != null) { + switch (function.type()) { + case BLOCK -> { + BlockParserFunction blockParserFunction = (BlockParserFunction) function; + Consumer consumer = blockParserFunction.accept(entry.getValue()); + blockBuilderConsumers.add(consumer); + } + case BASE_EFFECT -> { + BaseEffectParserFunction baseEffectParserFunction = (BaseEffectParserFunction) function; + Consumer consumer = baseEffectParserFunction.accept(entry.getValue()); + effectBuilderConsumers.add(consumer); + } + case LOOT -> { + LootParserFunction lootParserFunction = (LootParserFunction) function; + Consumer consumer = lootParserFunction.accept(entry.getValue()); + lootBuilderConsumers.add(consumer); + } + case EVENT -> { + EventParserFunction eventParserFunction = (EventParserFunction) function; + Consumer consumer = eventParserFunction.accept(entry.getValue()); + eventBuilderConsumers.add(consumer); + } + } + continue; + } + if (entry.getValue() instanceof Section innerSection) { + analyze(innerSection, node.getChildTree()); + } + } + } + + public BlockConfig getBlock() { + BlockConfig.Builder builder = BlockConfig.builder() + .id(id); + for (Consumer consumer : blockBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + private LootBaseEffect getBaseEffect() { + LootBaseEffect.Builder builder = LootBaseEffect.builder(); + for (Consumer consumer : effectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public Loot getLoot() { + Loot.Builder builder = Loot.builder() + .id(id) + .type(LootType.BLOCK) + .lootBaseEffect(getBaseEffect()); + for (Consumer consumer : lootBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public EventCarrier getEventCarrier() { + EventCarrier.Builder builder = EventCarrier.builder() + .id(id) + .type(MechanicType.BLOCK); + for (Consumer consumer : eventBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/ConfigManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/ConfigManager.java new file mode 100644 index 00000000..bbdfdba2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/ConfigManager.java @@ -0,0 +1,386 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; +import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; +import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; +import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; +import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.config.function.*; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; +import net.momirealms.customfishing.api.mechanic.effect.LootBaseEffect; +import net.momirealms.customfishing.api.mechanic.entity.EntityConfig; +import net.momirealms.customfishing.api.mechanic.event.EventCarrier; +import net.momirealms.customfishing.api.mechanic.hook.HookConfig; +import net.momirealms.customfishing.api.mechanic.loot.Loot; +import net.momirealms.customfishing.api.mechanic.requirement.Requirement; +import net.momirealms.customfishing.api.mechanic.totem.TotemConfig; +import net.momirealms.customfishing.common.config.ConfigLoader; +import net.momirealms.customfishing.common.config.node.Node; +import net.momirealms.customfishing.common.item.Item; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import net.momirealms.customfishing.common.util.Pair; +import net.momirealms.customfishing.common.util.TriConsumer; +import org.bukkit.entity.Player; +import org.bukkit.event.EventPriority; +import org.bukkit.inventory.ItemStack; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + +public abstract class ConfigManager implements ConfigLoader, Reloadable { + + private static ConfigManager instance; + protected final BukkitCustomFishingPlugin plugin; + protected final HashMap> formatFunctions = new HashMap<>(); + protected int placeholderLimit; + protected boolean redisRanking; + protected String serverGroup; + protected String[] itemDetectOrder = new String[0]; + protected String[] blockDetectOrder = new String[0]; + protected int dataSaveInterval; + protected boolean logDataSaving; + protected boolean lockData; + protected boolean metrics; + protected boolean checkUpdate; + protected boolean debug; + protected boolean overrideVanillaWaitTime; + protected int waterMinTime; + protected int waterMaxTime; + protected boolean enableLavaFishing; + protected int lavaMinTime; + protected int lavaMaxTime; + protected boolean enableVoidFishing; + protected int voidMinTime; + protected int voidMaxTime; + protected int multipleLootSpawnDelay; + protected boolean restrictedSizeRange; + protected List durabilityLore; + protected boolean allowMultipleTotemType; + protected boolean allowSameTotemType; + protected EventPriority eventPriority; + protected Requirement[] mechanicRequirements; + protected Requirement[] skipGameRequirements; + protected Requirement[] autoFishingRequirements; + protected boolean enableBag; + protected boolean baitAnimation; + protected List, Integer>> globalEffects; + + protected ConfigManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + instance = this; + } + + public static boolean debug() { + return instance.debug; + } + + public static int placeholderLimit() { + return instance.placeholderLimit; + } + + public static boolean redisRanking() { + return instance.redisRanking; + } + + public static String serverGroup() { + return instance.serverGroup; + } + + public static String[] itemDetectOrder() { + return instance.itemDetectOrder; + } + + public static String[] blockDetectOrder() { + return instance.blockDetectOrder; + } + + public static int dataSaveInterval() { + return instance.dataSaveInterval; + } + + public static boolean logDataSaving() { + return instance.logDataSaving; + } + + public static boolean lockData() { + return instance.lockData; + } + + public static boolean metrics() { + return instance.metrics; + } + + public static boolean checkUpdate() { + return instance.checkUpdate; + } + + public static boolean overrideVanillaWaitTime() { + return instance.overrideVanillaWaitTime; + } + + public static int waterMinTime() { + return instance.waterMinTime; + } + + public static int waterMaxTime() { + return instance.waterMaxTime; + } + + public static boolean enableLavaFishing() { + return instance.enableLavaFishing; + } + + public static int lavaMinTime() { + return instance.lavaMinTime; + } + + public static int lavaMaxTime() { + return instance.lavaMaxTime; + } + + public static boolean enableVoidFishing() { + return instance.enableVoidFishing; + } + + public static int voidMinTime() { + return instance.voidMinTime; + } + + public static int voidMaxTime() { + return instance.voidMaxTime; + } + + public static int multipleLootSpawnDelay() { + return instance.multipleLootSpawnDelay; + } + + public static boolean restrictedSizeRange() { + return instance.restrictedSizeRange; + } + + public static boolean allowMultipleTotemType() { + return instance.allowMultipleTotemType; + } + + public static boolean allowSameTotemType() { + return instance.allowSameTotemType; + } + + public static boolean enableBag() { + return instance.enableBag; + } + + public static boolean baitAnimation() { + return instance.baitAnimation; + } + + public static List durabilityLore() { + return instance.durabilityLore; + } + + public static EventPriority eventPriority() { + return instance.eventPriority; + } + + public static Requirement[] mechanicRequirements() { + return instance.mechanicRequirements; + } + + public static Requirement[] autoFishingRequirements() { + return instance.autoFishingRequirements; + } + + public static Requirement[] skipGameRequirements() { + return instance.skipGameRequirements; + } + + public static List, Integer>> globalEffects() { + return instance.globalEffects; + } + + public void registerHookParser(Function> function, String... nodes) { + registerNodeFunction(nodes, new HookParserFunction(function)); + } + + public void registerTotemParser(Function> function, String... nodes) { + registerNodeFunction(nodes, new TotemParserFunction(function)); + } + + public void registerLootParser(Function> function, String... nodes) { + registerNodeFunction(nodes, new LootParserFunction(function)); + } + + public void registerItemParser(Function, Context>> function, int priority, String... nodes) { + registerNodeFunction(nodes, new ItemParserFunction(priority, function)); + } + + public void registerEffectModifierParser(Function> function, String... nodes) { + registerNodeFunction(nodes, new EffectModifierParserFunction(function)); + } + + public void registerEntityParser(Function> function, String... nodes) { + registerNodeFunction(nodes, new EntityParserFunction(function)); + } + + public void registerEventParser(Function> function, String... nodes) { + registerNodeFunction(nodes, new EventParserFunction(function)); + } + + public void registerBaseEffectParser(Function> function, String... nodes) { + registerNodeFunction(nodes, new BaseEffectParserFunction(function)); + } + + public void unregisterNodeFunction(String... nodes) { + Map> functionMap = formatFunctions; + for (int i = 0; i < nodes.length; i++) { + if (functionMap.containsKey(nodes[i])) { + Node functionNode = functionMap.get(nodes[i]); + if (i != nodes.length - 1) { + if (functionNode.nodeValue() != null) { + return; + } else { + functionMap = functionNode.getChildTree(); + } + } else { + if (functionNode.nodeValue() != null) { + functionMap.remove(nodes[i]); + } + } + } + } + } + + public void registerNodeFunction(String[] nodes, ConfigParserFunction configParserFunction) { + Map> functionMap = formatFunctions; + for (int i = 0; i < nodes.length; i++) { + if (functionMap.containsKey(nodes[i])) { + Node functionNode = functionMap.get(nodes[i]); + if (functionNode.nodeValue() != null) { + throw new IllegalArgumentException("Format function '" + nodes[i] + "' already exists"); + } + functionMap = functionNode.getChildTree(); + } else { + if (i != nodes.length - 1) { + Node newNode = new Node<>(); + functionMap.put(nodes[i], newNode); + functionMap = newNode.getChildTree(); + } else { + functionMap.put(nodes[i], new Node<>(configParserFunction)); + } + } + } + } + + protected Path resolveConfig(String filePath) { + if (filePath == null || filePath.isEmpty()) { + throw new IllegalArgumentException("ResourcePath cannot be null or empty"); + } + filePath = filePath.replace('\\', '/'); + Path configFile = plugin.getConfigDirectory().resolve(filePath); + // if the config doesn't exist, create it based on the template in the resources dir + if (!Files.exists(configFile)) { + try { + Files.createDirectories(configFile.getParent()); + } catch (IOException e) { + // ignore + } + try (InputStream is = plugin.getResourceStream(filePath)) { + if (is == null) { + throw new IllegalArgumentException("The embedded resource '" + filePath + "' cannot be found"); + } + Files.copy(is, configFile); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return configFile; + } + + @Override + public YamlDocument loadConfig(String filePath) { + return loadConfig(filePath, '.'); + } + + @Override + public YamlDocument loadConfig(String filePath, char routeSeparator) { + try (InputStream inputStream = new FileInputStream(resolveConfig(filePath).toFile())) { + return YamlDocument.create( + inputStream, + plugin.getResourceStream(filePath), + GeneralSettings.builder().setRouteSeparator(routeSeparator).build(), + LoaderSettings + .builder() + .setAutoUpdate(true) + .build(), + DumperSettings.DEFAULT, + UpdaterSettings + .builder() + .setVersioning(new BasicVersioning("config-version")) + .build() + ); + } catch (IOException e) { + plugin.getPluginLogger().severe("Failed to load config " + filePath, e); + throw new RuntimeException(e); + } + } + + @Override + public YamlDocument loadData(File file) { + try (InputStream inputStream = new FileInputStream(file)) { + return YamlDocument.create(inputStream); + } catch (IOException e) { + plugin.getPluginLogger().severe("Failed to load config " + file, e); + throw new RuntimeException(e); + } + } + + @Override + public YamlDocument loadData(File file, char routeSeparator) { + try (InputStream inputStream = new FileInputStream(file)) { + return YamlDocument.create(inputStream, GeneralSettings.builder().setRouteSeparator(routeSeparator).build()); + } catch (IOException e) { + plugin.getPluginLogger().severe("Failed to load config " + file, e); + throw new RuntimeException(e); + } + } + + public Map> getFormatFunctions() { + return formatFunctions; + } + + public abstract List, Double, Double>>> parseWeightOperation(List ops); + + public abstract List, Double, Double>>> parseGroupWeightOperation(List gops); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/ConfigType.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/ConfigType.java new file mode 100644 index 00000000..87d6cab4 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/ConfigType.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.config.function.ConfigParserFunction; +import net.momirealms.customfishing.common.config.node.Node; +import org.apache.logging.log4j.util.TriConsumer; + +import java.util.Map; + +public class ConfigType { + + public static final ConfigType ITEM = of( + "item", + (id, section, functions) -> { + MechanicType.register(id, MechanicType.LOOT); + ItemConfigParser config = new ItemConfigParser(id, section, functions); + BukkitCustomFishingPlugin.getInstance().getItemManager().registerItem(config.getItem()); + BukkitCustomFishingPlugin.getInstance().getLootManager().registerLoot(config.getLoot()); + BukkitCustomFishingPlugin.getInstance().getEventManager().registerEventCarrier(config.getEventCarrier()); + } + ); + + public static final ConfigType ENTITY = of( + "entity", + (id, section, functions) -> { + MechanicType.register(id, MechanicType.ENTITY); + EntityConfigParser config = new EntityConfigParser(id, section, functions); + BukkitCustomFishingPlugin.getInstance().getEntityManager().registerEntity(config.getEntity()); + BukkitCustomFishingPlugin.getInstance().getLootManager().registerLoot(config.getLoot()); + BukkitCustomFishingPlugin.getInstance().getEventManager().registerEventCarrier(config.getEventCarrier()); + } + ); + + public static final ConfigType BLOCK = of( + "block", + (id, section, functions) -> { + MechanicType.register(id, MechanicType.BLOCK); + BlockConfigParser config = new BlockConfigParser(id, section, functions); + BukkitCustomFishingPlugin.getInstance().getBlockManager().registerBlock(config.getBlock()); + BukkitCustomFishingPlugin.getInstance().getLootManager().registerLoot(config.getLoot()); + BukkitCustomFishingPlugin.getInstance().getEventManager().registerEventCarrier(config.getEventCarrier()); + } + ); + + public static final ConfigType ROD = of( + "rod", + (id, section, functions) -> { + MechanicType.register(id, MechanicType.ROD); + RodConfigParser config = new RodConfigParser(id, section, functions); + BukkitCustomFishingPlugin.getInstance().getItemManager().registerItem(config.getItem()); + //BukkitCustomFishingPlugin.getInstance().getLootManager().registerLoot(config.getLoot()); + BukkitCustomFishingPlugin.getInstance().getEffectManager().registerEffectModifier(config.getEffectModifier(), MechanicType.ROD); + BukkitCustomFishingPlugin.getInstance().getEventManager().registerEventCarrier(config.getEventCarrier()); + } + ); + + public static final ConfigType BAIT = of( + "bait", + (id, section, functions) -> { + MechanicType.register(id, MechanicType.BAIT); + BaitConfigParser config = new BaitConfigParser(id, section, functions); + BukkitCustomFishingPlugin.getInstance().getItemManager().registerItem(config.getItem()); + //BukkitCustomFishingPlugin.getInstance().getLootManager().registerLoot(config.getLoot()); + BukkitCustomFishingPlugin.getInstance().getEffectManager().registerEffectModifier(config.getEffectModifier(), MechanicType.BAIT); + BukkitCustomFishingPlugin.getInstance().getEventManager().registerEventCarrier(config.getEventCarrier()); + } + ); + + public static final ConfigType HOOK = of( + "hook", + (id, section, functions) -> { + MechanicType.register(id, MechanicType.HOOK); + HookConfigParser config = new HookConfigParser(id, section, functions); + BukkitCustomFishingPlugin.getInstance().getItemManager().registerItem(config.getItem()); + //BukkitCustomFishingPlugin.getInstance().getLootManager().registerLoot(config.getLoot()); + BukkitCustomFishingPlugin.getInstance().getEffectManager().registerEffectModifier(config.getEffectModifier(), MechanicType.HOOK); + BukkitCustomFishingPlugin.getInstance().getEventManager().registerEventCarrier(config.getEventCarrier()); + BukkitCustomFishingPlugin.getInstance().getHookManager().registerHook(config.getHook()); + } + ); + + public static final ConfigType UTIL = of( + "util", + (id, section, functions) -> { + MechanicType.register(id, MechanicType.UTIL); + UtilConfigParser config = new UtilConfigParser(id, section, functions); + BukkitCustomFishingPlugin.getInstance().getItemManager().registerItem(config.getItem()); + //BukkitCustomFishingPlugin.getInstance().getLootManager().registerLoot(config.getLoot()); + BukkitCustomFishingPlugin.getInstance().getEffectManager().registerEffectModifier(config.getEffectModifier(), MechanicType.UTIL); + BukkitCustomFishingPlugin.getInstance().getEventManager().registerEventCarrier(config.getEventCarrier()); + } + ); + + public static final ConfigType TOTEM = of( + "totem", + (id, section, functions) -> { + TotemConfigParser config = new TotemConfigParser(id, section, functions); + BukkitCustomFishingPlugin.getInstance().getEffectManager().registerEffectModifier(config.getEffectModifier(), MechanicType.TOTEM); + BukkitCustomFishingPlugin.getInstance().getEventManager().registerEventCarrier(config.getEventCarrier()); + BukkitCustomFishingPlugin.getInstance().getTotemManager().registerTotem(config.getTotemConfig()); + } + ); + + public static final ConfigType ENCHANT = of( + "enchant", + (id, section, functions) -> { + EnchantConfigParser config = new EnchantConfigParser(id, section, functions); + BukkitCustomFishingPlugin.getInstance().getEffectManager().registerEffectModifier(config.getEffectModifier(), MechanicType.ENCHANT); + BukkitCustomFishingPlugin.getInstance().getEventManager().registerEventCarrier(config.getEventCarrier()); + } + ); + + public static final ConfigType MINI_GAME = of( + "minigame", + (id, section, functions) -> { + MiniGameConfigParser config = new MiniGameConfigParser(id, section); + BukkitCustomFishingPlugin.getInstance().getGameManager().registerGame(config.getGame()); + } + ); + + private static final ConfigType[] values = new ConfigType[] {ITEM, ENTITY, BLOCK, HOOK, ROD, BAIT, UTIL, TOTEM, ENCHANT, MINI_GAME}; + + public static ConfigType[] values() { + return values; + } + + private final String path; + private TriConsumer>> argumentConsumer; + + public ConfigType(String path, TriConsumer>> argumentConsumer) { + this.path = path; + this.argumentConsumer = argumentConsumer; + } + + public void argumentConsumer(TriConsumer>> argumentConsumer) { + this.argumentConsumer = argumentConsumer; + } + + public static ConfigType of(String path, TriConsumer>> argumentConsumer) { + return new ConfigType(path, argumentConsumer); + } + + public void parse(String id, Section section, Map> functions) { + argumentConsumer.accept(id, section, functions); + } + + public String path() { + return path; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/EnchantConfigParser.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/EnchantConfigParser.java new file mode 100644 index 00000000..56ce60e0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/EnchantConfigParser.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.config.function.ConfigParserFunction; +import net.momirealms.customfishing.api.mechanic.config.function.EffectModifierParserFunction; +import net.momirealms.customfishing.api.mechanic.config.function.EventParserFunction; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; +import net.momirealms.customfishing.api.mechanic.event.EventCarrier; +import net.momirealms.customfishing.common.config.node.Node; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class EnchantConfigParser { + + private final String id; + private final List> eventBuilderConsumers = new ArrayList<>(); + private final List> effectBuilderConsumers = new ArrayList<>(); + + public EnchantConfigParser(String id, Section section, Map> functionMap) { + this.id = id; + analyze(section, functionMap); + } + + private void analyze(Section section, Map> functionMap) { + Map dataMap = section.getStringRouteMappedValues(false); + for (Map.Entry entry : dataMap.entrySet()) { + String key = entry.getKey(); + Node node = functionMap.get(key); + if (node == null) continue; + ConfigParserFunction function = node.nodeValue(); + if (function != null) { + switch (function.type()) { + case EVENT -> { + EventParserFunction eventParserFunction = (EventParserFunction) function; + Consumer consumer = eventParserFunction.accept(entry.getValue()); + eventBuilderConsumers.add(consumer); + } + case EFFECT_MODIFIER -> { + EffectModifierParserFunction effectModifierParserFunction = (EffectModifierParserFunction) function; + Consumer consumer = effectModifierParserFunction.accept(entry.getValue()); + effectBuilderConsumers.add(consumer); + } + } + continue; + } + if (entry.getValue() instanceof Section innerSection) { + analyze(innerSection, node.getChildTree()); + } + } + } + + public EventCarrier getEventCarrier() { + EventCarrier.Builder builder = EventCarrier.builder() + .id(id) + .type(MechanicType.ENCHANT); + for (Consumer consumer : eventBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public EffectModifier getEffectModifier() { + EffectModifier.Builder builder = EffectModifier.builder() + .id(id) + .type(MechanicType.ENCHANT); + for (Consumer consumer : effectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/EntityConfigParser.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/EntityConfigParser.java new file mode 100644 index 00000000..ec6e1522 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/EntityConfigParser.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.config.function.*; +import net.momirealms.customfishing.api.mechanic.effect.LootBaseEffect; +import net.momirealms.customfishing.api.mechanic.entity.EntityConfig; +import net.momirealms.customfishing.api.mechanic.event.EventCarrier; +import net.momirealms.customfishing.api.mechanic.loot.Loot; +import net.momirealms.customfishing.api.mechanic.loot.LootType; +import net.momirealms.customfishing.common.config.node.Node; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class EntityConfigParser { + + private final String id; + private final List> entityBuilderConsumers = new ArrayList<>(); + private final List> effectBuilderConsumers = new ArrayList<>(); + private final List> lootBuilderConsumers = new ArrayList<>(); + private final List> eventBuilderConsumers = new ArrayList<>(); + + public EntityConfigParser(String id, Section section, Map> functionMap) { + this.id = id; + analyze(section, functionMap); + } + + private void analyze(Section section, Map> functionMap) { + Map dataMap = section.getStringRouteMappedValues(false); + for (Map.Entry entry : dataMap.entrySet()) { + String key = entry.getKey(); + Node node = functionMap.get(key); + if (node == null) continue; + ConfigParserFunction function = node.nodeValue(); + if (function != null) { + switch (function.type()) { + case ENTITY -> { + EntityParserFunction entityParserFunction = (EntityParserFunction) function; + Consumer consumer = entityParserFunction.accept(entry.getValue()); + entityBuilderConsumers.add(consumer); + } + case BASE_EFFECT -> { + BaseEffectParserFunction baseEffectParserFunction = (BaseEffectParserFunction) function; + Consumer consumer = baseEffectParserFunction.accept(entry.getValue()); + effectBuilderConsumers.add(consumer); + } + case LOOT -> { + LootParserFunction lootParserFunction = (LootParserFunction) function; + Consumer consumer = lootParserFunction.accept(entry.getValue()); + lootBuilderConsumers.add(consumer); + } + case EVENT -> { + EventParserFunction eventParserFunction = (EventParserFunction) function; + Consumer consumer = eventParserFunction.accept(entry.getValue()); + eventBuilderConsumers.add(consumer); + } + } + continue; + } + if (entry.getValue() instanceof Section innerSection) { + analyze(innerSection, node.getChildTree()); + } + } + } + + public EntityConfig getEntity() { + EntityConfig.Builder builder = EntityConfig.builder() + .id(id); + for (Consumer consumer : entityBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + private LootBaseEffect getBaseEffect() { + LootBaseEffect.Builder builder = LootBaseEffect.builder(); + for (Consumer consumer : effectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public Loot getLoot() { + Loot.Builder builder = Loot.builder() + .id(id) + .type(LootType.ENTITY) + .lootBaseEffect(getBaseEffect()); + for (Consumer consumer : lootBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public EventCarrier getEventCarrier() { + EventCarrier.Builder builder = EventCarrier.builder() + .id(id) + .type(MechanicType.ENTITY); + for (Consumer consumer : eventBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/GUIItemParser.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/GUIItemParser.java new file mode 100644 index 00000000..4c981e10 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/GUIItemParser.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.config.function.ConfigParserFunction; +import net.momirealms.customfishing.api.mechanic.config.function.ItemParserFunction; +import net.momirealms.customfishing.api.mechanic.config.function.PriorityFunction; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.item.CustomFishingItem; +import net.momirealms.customfishing.common.config.node.Node; +import net.momirealms.customfishing.common.item.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +public class GUIItemParser { + + private final String id; + private final String material; + private final List, Context>>> tagConsumers = new ArrayList<>(); + + public GUIItemParser(String id, Section section, Map> functionMap) { + this.id = id; + this.material = section.getString("material"); + analyze(section, functionMap); + } + + private void analyze(Section section, Map> functionMap) { + Map dataMap = section.getStringRouteMappedValues(false); + for (Map.Entry entry : dataMap.entrySet()) { + String key = entry.getKey(); + Node node = functionMap.get(key); + if (node == null) continue; + ConfigParserFunction function = node.nodeValue(); + if (function != null) { + if (function instanceof ItemParserFunction propertyFunction) { + BiConsumer, Context> result = propertyFunction.accept(entry.getValue()); + tagConsumers.add(new PriorityFunction<>(propertyFunction.getPriority(), result)); + } + continue; + } + if (entry.getValue() instanceof Section innerSection) { + analyze(innerSection, node.getChildTree()); + } + } + } + + public CustomFishingItem getItem() { + return CustomFishingItem.builder() + .material(material) + .id(id) + .tagConsumers(tagConsumers) + .build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/HookConfigParser.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/HookConfigParser.java new file mode 100644 index 00000000..a6426132 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/HookConfigParser.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.config.function.*; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; +import net.momirealms.customfishing.api.mechanic.effect.LootBaseEffect; +import net.momirealms.customfishing.api.mechanic.event.EventCarrier; +import net.momirealms.customfishing.api.mechanic.hook.HookConfig; +import net.momirealms.customfishing.api.mechanic.item.CustomFishingItem; +import net.momirealms.customfishing.api.mechanic.loot.Loot; +import net.momirealms.customfishing.api.mechanic.loot.LootType; +import net.momirealms.customfishing.common.config.node.Node; +import net.momirealms.customfishing.common.item.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class HookConfigParser { + + private final String id; + private final String material; + private final List, Context>>> tagConsumers = new ArrayList<>(); + private final List> eventBuilderConsumers = new ArrayList<>(); + private final List> hookBuilderConsumers = new ArrayList<>(); + private final List> effectBuilderConsumers = new ArrayList<>(); + private final List> baseEffectBuilderConsumers = new ArrayList<>(); + private final List> lootBuilderConsumers = new ArrayList<>(); + + public HookConfigParser(String id, Section section, Map> functionMap) { + this.id = id; + this.material = section.getString("material"); + if (!section.contains("tag")) section.set("tag", true); + analyze(section, functionMap); + } + + private void analyze(Section section, Map> functionMap) { + Map dataMap = section.getStringRouteMappedValues(false); + for (Map.Entry entry : dataMap.entrySet()) { + String key = entry.getKey(); + Node node = functionMap.get(key); + if (node == null) continue; + ConfigParserFunction function = node.nodeValue(); + if (function != null) { + switch (function.type()) { + case BASE_EFFECT -> { + BaseEffectParserFunction baseEffectParserFunction = (BaseEffectParserFunction) function; + Consumer consumer = baseEffectParserFunction.accept(entry.getValue()); + baseEffectBuilderConsumers.add(consumer); + } + case LOOT -> { + LootParserFunction lootParserFunction = (LootParserFunction) function; + Consumer consumer = lootParserFunction.accept(entry.getValue()); + lootBuilderConsumers.add(consumer); + } + case ITEM -> { + ItemParserFunction propertyFunction = (ItemParserFunction) function; + BiConsumer, Context> result = propertyFunction.accept(entry.getValue()); + tagConsumers.add(new PriorityFunction<>(propertyFunction.getPriority(), result)); + } + case EVENT -> { + EventParserFunction eventParserFunction = (EventParserFunction) function; + Consumer consumer = eventParserFunction.accept(entry.getValue()); + eventBuilderConsumers.add(consumer); + } + case EFFECT_MODIFIER -> { + EffectModifierParserFunction effectModifierParserFunction = (EffectModifierParserFunction) function; + Consumer consumer = effectModifierParserFunction.accept(entry.getValue()); + effectBuilderConsumers.add(consumer); + } + case HOOK -> { + HookParserFunction hookParserFunction = (HookParserFunction) function; + Consumer consumer = hookParserFunction.accept(entry.getValue()); + hookBuilderConsumers.add(consumer); + } + } + continue; + } + if (entry.getValue() instanceof Section innerSection) { + analyze(innerSection, node.getChildTree()); + } + } + } + + public CustomFishingItem getItem() { + return CustomFishingItem.builder() + .material(material) + .id(id) + .tagConsumers(tagConsumers) + .build(); + } + + public EventCarrier getEventCarrier() { + EventCarrier.Builder builder = EventCarrier.builder() + .id(id) + .type(MechanicType.HOOK); + for (Consumer consumer : eventBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public EffectModifier getEffectModifier() { + EffectModifier.Builder builder = EffectModifier.builder() + .id(id) + .type(MechanicType.HOOK); + for (Consumer consumer : effectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public HookConfig getHook() { + HookConfig.Builder builder = HookConfig.builder() + .id(id); + for (Consumer consumer : hookBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + private LootBaseEffect getBaseEffect() { + LootBaseEffect.Builder builder = LootBaseEffect.builder(); + for (Consumer consumer : baseEffectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public Loot getLoot() { + Loot.Builder builder = Loot.builder() + .id(id) + .type(LootType.ITEM) + .lootBaseEffect(getBaseEffect()); + for (Consumer consumer : lootBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/ItemConfigParser.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/ItemConfigParser.java new file mode 100644 index 00000000..9347b54e --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/ItemConfigParser.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.config.function.*; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.effect.LootBaseEffect; +import net.momirealms.customfishing.api.mechanic.event.EventCarrier; +import net.momirealms.customfishing.api.mechanic.item.CustomFishingItem; +import net.momirealms.customfishing.api.mechanic.loot.Loot; +import net.momirealms.customfishing.api.mechanic.loot.LootType; +import net.momirealms.customfishing.common.config.node.Node; +import net.momirealms.customfishing.common.item.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class ItemConfigParser { + + private final String id; + private final String material; + private final List, Context>>> tagConsumers = new ArrayList<>(); + private final List> effectBuilderConsumers = new ArrayList<>(); + private final List> lootBuilderConsumers = new ArrayList<>(); + private final List> eventBuilderConsumers = new ArrayList<>(); + + public ItemConfigParser(String id, Section section, Map> functionMap) { + this.id = id; + this.material = section.getString("material"); + if (!section.contains("tag")) section.set("tag", true); + if (!section.contains("nick")) { + if (section.contains("display.name")) { + section.set("nick", section.getString("display.name")); + } + } + analyze(section, functionMap); + } + + private void analyze(Section section, Map> functionMap) { + Map dataMap = section.getStringRouteMappedValues(false); + for (Map.Entry entry : dataMap.entrySet()) { + String key = entry.getKey(); + Node node = functionMap.get(key); + if (node == null) continue; + ConfigParserFunction function = node.nodeValue(); + if (function != null) { + switch (function.type()) { + case ITEM -> { + ItemParserFunction propertyFunction = (ItemParserFunction) function; + BiConsumer, Context> result = propertyFunction.accept(entry.getValue()); + tagConsumers.add(new PriorityFunction<>(propertyFunction.getPriority(), result)); + } + case BASE_EFFECT -> { + BaseEffectParserFunction baseEffectParserFunction = (BaseEffectParserFunction) function; + Consumer consumer = baseEffectParserFunction.accept(entry.getValue()); + effectBuilderConsumers.add(consumer); + } + case LOOT -> { + LootParserFunction lootParserFunction = (LootParserFunction) function; + Consumer consumer = lootParserFunction.accept(entry.getValue()); + lootBuilderConsumers.add(consumer); + } + case EVENT -> { + EventParserFunction eventParserFunction = (EventParserFunction) function; + Consumer consumer = eventParserFunction.accept(entry.getValue()); + eventBuilderConsumers.add(consumer); + } + } + continue; + } + if (entry.getValue() instanceof Section innerSection) { + analyze(innerSection, node.getChildTree()); + } + } + } + + public CustomFishingItem getItem() { + return CustomFishingItem.builder() + .material(material) + .id(id) + .tagConsumers(tagConsumers) + .build(); + } + + private LootBaseEffect getBaseEffect() { + LootBaseEffect.Builder builder = LootBaseEffect.builder(); + for (Consumer consumer : effectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public Loot getLoot() { + Loot.Builder builder = Loot.builder() + .id(id) + .type(LootType.ITEM) + .lootBaseEffect(getBaseEffect()); + for (Consumer consumer : lootBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public EventCarrier getEventCarrier() { + EventCarrier.Builder builder = EventCarrier.builder() + .id(id) + .type(MechanicType.LOOT); + for (Consumer consumer : eventBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/MiniGameConfigParser.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/MiniGameConfigParser.java new file mode 100644 index 00000000..73cb6507 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/MiniGameConfigParser.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.game.Game; +import net.momirealms.customfishing.api.mechanic.game.GameFactory; + +public class MiniGameConfigParser { + + private final String id; + private Game game; + + public MiniGameConfigParser(String id, Section section) { + this.id = id; + analyze(section); + } + + private void analyze(Section section) { + String type = section.getString("game-type"); + GameFactory factory = BukkitCustomFishingPlugin.getInstance().getGameManager().getGameFactory(type); + if (factory == null) { + throw new RuntimeException("Unknown game-type: " + type); + } + this.game = factory.create(id, section); + } + + public Game getGame() { + return game; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/RodConfigParser.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/RodConfigParser.java new file mode 100644 index 00000000..1b1bc19a --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/RodConfigParser.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.config.function.*; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; +import net.momirealms.customfishing.api.mechanic.effect.LootBaseEffect; +import net.momirealms.customfishing.api.mechanic.event.EventCarrier; +import net.momirealms.customfishing.api.mechanic.item.CustomFishingItem; +import net.momirealms.customfishing.api.mechanic.loot.Loot; +import net.momirealms.customfishing.api.mechanic.loot.LootType; +import net.momirealms.customfishing.common.config.node.Node; +import net.momirealms.customfishing.common.item.Item; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class RodConfigParser { + + private final String id; + private final String material; + private final List, Context>>> tagConsumers = new ArrayList<>(); + private final List> eventBuilderConsumers = new ArrayList<>(); + private final List> effectBuilderConsumers = new ArrayList<>(); + private final List> baseEffectBuilderConsumers = new ArrayList<>(); + private final List> lootBuilderConsumers = new ArrayList<>(); + + public RodConfigParser(String id, Section section, Map> functionMap) { + this.id = id; + this.material = section.contains("material") ? section.getString("material") : Material.FISHING_ROD.name(); + if (!section.contains("tag")) section.set("tag", true); + analyze(section, functionMap); + } + + private void analyze(Section section, Map> functionMap) { + Map dataMap = section.getStringRouteMappedValues(false); + for (Map.Entry entry : dataMap.entrySet()) { + String key = entry.getKey(); + Node node = functionMap.get(key); + if (node == null) continue; + ConfigParserFunction function = node.nodeValue(); + if (function != null) { + switch (function.type()) { + case BASE_EFFECT -> { + BaseEffectParserFunction baseEffectParserFunction = (BaseEffectParserFunction) function; + Consumer consumer = baseEffectParserFunction.accept(entry.getValue()); + baseEffectBuilderConsumers.add(consumer); + } + case LOOT -> { + LootParserFunction lootParserFunction = (LootParserFunction) function; + Consumer consumer = lootParserFunction.accept(entry.getValue()); + lootBuilderConsumers.add(consumer); + } + case ITEM -> { + ItemParserFunction propertyFunction = (ItemParserFunction) function; + BiConsumer, Context> result = propertyFunction.accept(entry.getValue()); + tagConsumers.add(new PriorityFunction<>(propertyFunction.getPriority(), result)); + } + case EVENT -> { + EventParserFunction eventParserFunction = (EventParserFunction) function; + Consumer consumer = eventParserFunction.accept(entry.getValue()); + eventBuilderConsumers.add(consumer); + } + case EFFECT_MODIFIER -> { + EffectModifierParserFunction effectModifierParserFunction = (EffectModifierParserFunction) function; + Consumer consumer = effectModifierParserFunction.accept(entry.getValue()); + effectBuilderConsumers.add(consumer); + } + } + continue; + } + if (entry.getValue() instanceof Section innerSection) { + analyze(innerSection, node.getChildTree()); + } + } + } + + public CustomFishingItem getItem() { + return CustomFishingItem.builder() + .material(material) + .id(id) + .tagConsumers(tagConsumers) + .build(); + } + + public EventCarrier getEventCarrier() { + EventCarrier.Builder builder = EventCarrier.builder() + .id(id) + .type(MechanicType.ROD); + for (Consumer consumer : eventBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public EffectModifier getEffectModifier() { + EffectModifier.Builder builder = EffectModifier.builder() + .id(id) + .type(MechanicType.ROD); + for (Consumer consumer : effectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + private LootBaseEffect getBaseEffect() { + LootBaseEffect.Builder builder = LootBaseEffect.builder(); + for (Consumer consumer : baseEffectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public Loot getLoot() { + Loot.Builder builder = Loot.builder() + .id(id) + .type(LootType.ITEM) + .lootBaseEffect(getBaseEffect()); + for (Consumer consumer : lootBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/TotemConfigParser.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/TotemConfigParser.java new file mode 100644 index 00000000..555bb78b --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/TotemConfigParser.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.config.function.ConfigParserFunction; +import net.momirealms.customfishing.api.mechanic.config.function.EffectModifierParserFunction; +import net.momirealms.customfishing.api.mechanic.config.function.EventParserFunction; +import net.momirealms.customfishing.api.mechanic.config.function.TotemParserFunction; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; +import net.momirealms.customfishing.api.mechanic.event.EventCarrier; +import net.momirealms.customfishing.api.mechanic.totem.TotemConfig; +import net.momirealms.customfishing.common.config.node.Node; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class TotemConfigParser { + + private final String id; + private final List> eventBuilderConsumers = new ArrayList<>(); + private final List> effectBuilderConsumers = new ArrayList<>(); + private final List> totemBuilderConsumers = new ArrayList<>(); + + public TotemConfigParser(String id, Section section, Map> functionMap) { + this.id = id; + analyze(section, functionMap); + } + + private void analyze(Section section, Map> functionMap) { + Map dataMap = section.getStringRouteMappedValues(false); + for (Map.Entry entry : dataMap.entrySet()) { + String key = entry.getKey(); + Node node = functionMap.get(key); + if (node == null) continue; + ConfigParserFunction function = node.nodeValue(); + if (function != null) { + switch (function.type()) { + case EVENT -> { + EventParserFunction eventParserFunction = (EventParserFunction) function; + Consumer consumer = eventParserFunction.accept(entry.getValue()); + eventBuilderConsumers.add(consumer); + } + case EFFECT_MODIFIER -> { + EffectModifierParserFunction effectModifierParserFunction = (EffectModifierParserFunction) function; + Consumer consumer = effectModifierParserFunction.accept(entry.getValue()); + effectBuilderConsumers.add(consumer); + } + case TOTEM -> { + TotemParserFunction totemParserFunction = (TotemParserFunction) function; + Consumer consumer = totemParserFunction.accept(entry.getValue()); + totemBuilderConsumers.add(consumer); + } + } + continue; + } + if (entry.getValue() instanceof Section innerSection) { + analyze(innerSection, node.getChildTree()); + } + } + } + + public EventCarrier getEventCarrier() { + EventCarrier.Builder builder = EventCarrier.builder() + .id(id) + .type(MechanicType.TOTEM); + for (Consumer consumer : eventBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public EffectModifier getEffectModifier() { + EffectModifier.Builder builder = EffectModifier.builder() + .id(id) + .type(MechanicType.TOTEM); + for (Consumer consumer : effectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public TotemConfig getTotemConfig() { + TotemConfig.Builder builder = TotemConfig.builder() + .id(id); + for (Consumer consumer : totemBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/UtilConfigParser.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/UtilConfigParser.java new file mode 100644 index 00000000..5434d643 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/UtilConfigParser.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.config.function.*; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; +import net.momirealms.customfishing.api.mechanic.effect.LootBaseEffect; +import net.momirealms.customfishing.api.mechanic.event.EventCarrier; +import net.momirealms.customfishing.api.mechanic.item.CustomFishingItem; +import net.momirealms.customfishing.api.mechanic.loot.Loot; +import net.momirealms.customfishing.api.mechanic.loot.LootType; +import net.momirealms.customfishing.common.config.node.Node; +import net.momirealms.customfishing.common.item.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class UtilConfigParser { + + private final String id; + private final String material; + private final List, Context>>> tagConsumers = new ArrayList<>(); + private final List> eventBuilderConsumers = new ArrayList<>(); + private final List> effectBuilderConsumers = new ArrayList<>(); + private final List> baseEffectBuilderConsumers = new ArrayList<>(); + private final List> lootBuilderConsumers = new ArrayList<>(); + + public UtilConfigParser(String id, Section section, Map> functionMap) { + this.id = id; + this.material = section.getString("material"); + if (!section.contains("tag")) section.set("tag", true); + analyze(section, functionMap); + } + + private void analyze(Section section, Map> functionMap) { + Map dataMap = section.getStringRouteMappedValues(false); + for (Map.Entry entry : dataMap.entrySet()) { + String key = entry.getKey(); + Node node = functionMap.get(key); + if (node == null) continue; + ConfigParserFunction function = node.nodeValue(); + if (function != null) { + switch (function.type()) { + case BASE_EFFECT -> { + BaseEffectParserFunction baseEffectParserFunction = (BaseEffectParserFunction) function; + Consumer consumer = baseEffectParserFunction.accept(entry.getValue()); + baseEffectBuilderConsumers.add(consumer); + } + case LOOT -> { + LootParserFunction lootParserFunction = (LootParserFunction) function; + Consumer consumer = lootParserFunction.accept(entry.getValue()); + lootBuilderConsumers.add(consumer); + } + case ITEM -> { + ItemParserFunction propertyFunction = (ItemParserFunction) function; + BiConsumer, Context> result = propertyFunction.accept(entry.getValue()); + tagConsumers.add(new PriorityFunction<>(propertyFunction.getPriority(), result)); + } + case EVENT -> { + EventParserFunction eventParserFunction = (EventParserFunction) function; + Consumer consumer = eventParserFunction.accept(entry.getValue()); + eventBuilderConsumers.add(consumer); + } + case EFFECT_MODIFIER -> { + EffectModifierParserFunction effectModifierParserFunction = (EffectModifierParserFunction) function; + Consumer consumer = effectModifierParserFunction.accept(entry.getValue()); + effectBuilderConsumers.add(consumer); + } + } + continue; + } + if (entry.getValue() instanceof Section innerSection) { + analyze(innerSection, node.getChildTree()); + } + } + } + + public CustomFishingItem getItem() { + return CustomFishingItem.builder() + .material(material) + .id(id) + .tagConsumers(tagConsumers) + .build(); + } + + public EventCarrier getEventCarrier() { + EventCarrier.Builder builder = EventCarrier.builder() + .id(id) + .type(MechanicType.UTIL); + for (Consumer consumer : eventBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public EffectModifier getEffectModifier() { + EffectModifier.Builder builder = EffectModifier.builder() + .id(id); + for (Consumer consumer : effectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + private LootBaseEffect getBaseEffect() { + LootBaseEffect.Builder builder = LootBaseEffect.builder(); + for (Consumer consumer : baseEffectBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } + + public Loot getLoot() { + Loot.Builder builder = Loot.builder() + .id(id) + .type(LootType.ITEM) + .lootBaseEffect(getBaseEffect()); + for (Consumer consumer : lootBuilderConsumers) { + consumer.accept(builder); + } + return builder.build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/BaseEffectParserFunction.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/BaseEffectParserFunction.java new file mode 100644 index 00000000..d76358d1 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/BaseEffectParserFunction.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config.function; + +import net.momirealms.customfishing.api.mechanic.effect.LootBaseEffect; + +import java.util.function.Consumer; +import java.util.function.Function; + +public class BaseEffectParserFunction implements ConfigParserFunction { + + private final Function> function; + + public BaseEffectParserFunction(Function> function) { + this.function = function; + } + + public Consumer accept(Object object) { + return function.apply(object); + } + + @Override + public ParserType type() { + return ParserType.BASE_EFFECT; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/BlockParserFunction.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/BlockParserFunction.java new file mode 100644 index 00000000..22e27322 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/BlockParserFunction.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config.function; + +import net.momirealms.customfishing.api.mechanic.block.BlockConfig; + +import java.util.function.Consumer; +import java.util.function.Function; + +public class BlockParserFunction implements ConfigParserFunction { + + private final Function> function; + + public BlockParserFunction(Function> function) { + this.function = function; + } + + public Consumer accept(Object object) { + return function.apply(object); + } + + @Override + public ParserType type() { + return ParserType.BLOCK; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/ConfigParserFunction.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/ConfigParserFunction.java new file mode 100644 index 00000000..42c64dd9 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/ConfigParserFunction.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config.function; + +public interface ConfigParserFunction { + + ParserType type(); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/EffectModifierParserFunction.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/EffectModifierParserFunction.java new file mode 100644 index 00000000..7f16e816 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/EffectModifierParserFunction.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config.function; + +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; + +import java.util.function.Consumer; +import java.util.function.Function; + +public class EffectModifierParserFunction implements ConfigParserFunction { + + private final Function> function; + + public EffectModifierParserFunction(Function> function) { + this.function = function; + } + + public Consumer accept(Object object) { + return function.apply(object); + } + + @Override + public ParserType type() { + return ParserType.EFFECT_MODIFIER; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/EntityParserFunction.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/EntityParserFunction.java new file mode 100644 index 00000000..203220d6 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/EntityParserFunction.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config.function; + +import net.momirealms.customfishing.api.mechanic.entity.EntityConfig; + +import java.util.function.Consumer; +import java.util.function.Function; + +public class EntityParserFunction implements ConfigParserFunction { + + private final Function> function; + + public EntityParserFunction(Function> function) { + this.function = function; + } + + public Consumer accept(Object object) { + return function.apply(object); + } + + @Override + public ParserType type() { + return ParserType.ENTITY; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/EventParserFunction.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/EventParserFunction.java new file mode 100644 index 00000000..1e048515 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/EventParserFunction.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config.function; + +import net.momirealms.customfishing.api.mechanic.event.EventCarrier; + +import java.util.function.Consumer; +import java.util.function.Function; + +public class EventParserFunction implements ConfigParserFunction { + + private final Function> function; + + public EventParserFunction(Function> function) { + this.function = function; + } + + public Consumer accept(Object object) { + return function.apply(object); + } + + @Override + public ParserType type() { + return ParserType.EVENT; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/HookParserFunction.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/HookParserFunction.java new file mode 100644 index 00000000..6ecf66e7 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/HookParserFunction.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config.function; + +import net.momirealms.customfishing.api.mechanic.hook.HookConfig; + +import java.util.function.Consumer; +import java.util.function.Function; + +public class HookParserFunction implements ConfigParserFunction { + + private final Function> function; + + public HookParserFunction(Function> function) { + this.function = function; + } + + public Consumer accept(Object object) { + return function.apply(object); + } + + @Override + public ParserType type() { + return ParserType.HOOK; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/ItemParserFunction.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/ItemParserFunction.java new file mode 100644 index 00000000..196526b7 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/ItemParserFunction.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config.function; + +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.item.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +public class ItemParserFunction implements ConfigParserFunction { + + private final Function, Context>> function; + private final int priority; + + public ItemParserFunction(int priority, Function, Context>> function) { + this.function = function; + this.priority = priority; + } + + public BiConsumer, Context> accept(Object object) { + return function.apply(object); + } + + public int getPriority() { + return priority; + } + + @Override + public ParserType type() { + return ParserType.ITEM; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/LootParserFunction.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/LootParserFunction.java new file mode 100644 index 00000000..83451837 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/LootParserFunction.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config.function; + +import net.momirealms.customfishing.api.mechanic.loot.Loot; + +import java.util.function.Consumer; +import java.util.function.Function; + +public class LootParserFunction implements ConfigParserFunction { + + private final Function> function; + + public LootParserFunction(Function> function) { + this.function = function; + } + + public Consumer accept(Object object) { + return function.apply(object); + } + + @Override + public ParserType type() { + return ParserType.LOOT; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/Value.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/ParserType.java similarity index 77% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/Value.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/ParserType.java index 1dbea0c4..338cff10 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/Value.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/ParserType.java @@ -15,13 +15,16 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.mechanic.misc; +package net.momirealms.customfishing.api.mechanic.config.function; -import org.bukkit.entity.Player; - -import java.util.Map; - -public interface Value { - - double get(Player player, Map values); +public enum ParserType { + ITEM, + EFFECT_MODIFIER, + BASE_EFFECT, + LOOT, + EVENT, + ENTITY, + HOOK, + BLOCK, + TOTEM } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/ExpressionValue.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/PriorityFunction.java similarity index 57% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/ExpressionValue.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/PriorityFunction.java index f0daf218..09a13dcd 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/ExpressionValue.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/PriorityFunction.java @@ -15,24 +15,26 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.misc.value; +package net.momirealms.customfishing.api.mechanic.config.function; -import net.momirealms.customfishing.api.mechanic.misc.Value; -import net.momirealms.customfishing.util.ConfigUtils; -import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -import java.util.Map; +public class PriorityFunction implements Comparable> { -public class ExpressionValue implements Value { + private final int priority; + private final T function; - private final String expression; + public PriorityFunction(int priority, T function) { + this.priority = priority; + this.function = function; + } - public ExpressionValue(String expression) { - this.expression = expression; + public T get() { + return function; } @Override - public double get(Player player, Map values) { - return ConfigUtils.getExpressionValue(player, expression, values); + public int compareTo(@NotNull PriorityFunction o) { + return Integer.compare(this.priority, o.priority); } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/TotemParserFunction.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/TotemParserFunction.java new file mode 100644 index 00000000..c7d47a1a --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/config/function/TotemParserFunction.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.config.function; + +import net.momirealms.customfishing.api.mechanic.totem.TotemConfig; + +import java.util.function.Consumer; +import java.util.function.Function; + +public class TotemParserFunction implements ConfigParserFunction { + + private final Function> function; + + public TotemParserFunction(Function> function) { + this.function = function; + } + + public Consumer accept(Object object) { + return function.apply(object); + } + + @Override + public ParserType type() { + return ParserType.TOTEM; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/Context.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/Context.java new file mode 100644 index 00000000..460900e4 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/Context.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.context; + +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +/** + * The Context interface represents a generic context for custom fishing mechanics. + * It allows for storing and retrieving arguments, as well as getting the holder + * of the context. This can be used to maintain state or pass parameters within + * the custom fishing mechanics. + * + * @param the type of the holder object for this context + */ +public interface Context { + + /** + * Retrieves the map of arguments associated with this context. + * + * @return a map where the keys are argument names and the values are argument values. + */ + Map, Object> args(); + + /** + * Converts the context to a map of placeholders + * + * @return a map of placeholders + */ + Map placeholderMap(); + + /** + * Adds or updates an argument in the context. + * This method allows adding a new argument or updating the value of an existing argument. + * + * @param the type of the value being added to the context. + * @param key the ContextKeys key representing the argument to be added or updated. + * @param value the value to be associated with the specified key. + * @return the current context instance, allowing for method chaining. + */ + Context arg(ContextKeys key, C value); + + /** + * Combines one context with another + * + * @param other other + * @return this context + */ + Context combine(Context other); + + /** + * Retrieves the value of a specific argument from the context. + * This method fetches the value associated with the specified ContextKeys key. + * + * @param the type of the value being retrieved. + * @param key the ContextKeys key representing the argument to be retrieved. + * @return the value associated with the specified key, or null if the key does not exist. + */ + @Nullable + C arg(ContextKeys key); + + @Nullable + C remove(ContextKeys key); + + /** + * Gets the holder of this context. + * + * @return the holder object of type T. + */ + T getHolder(); + + /** + * Creates a player-specific context. + * + * @param player the player to be used as the holder of the context. + * @return a new Context instance with the specified player as the holder. + */ + static Context player(@Nullable Player player) { + return new PlayerContextImpl(player, false); + } + + static Context player(@Nullable Player player, boolean threadSafe) { + return new PlayerContextImpl(player, threadSafe); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/ContextKeys.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/ContextKeys.java new file mode 100644 index 00000000..eefc8aa4 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/ContextKeys.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.context; + +import net.momirealms.customfishing.api.mechanic.competition.CompetitionGoal; +import net.momirealms.customfishing.api.mechanic.loot.LootType; +import org.bukkit.Location; + +import java.util.Objects; + +public class ContextKeys { + + public static final ContextKeys LOCATION = of("location", Location.class); + public static final ContextKeys X = of("x", Integer.class); + public static final ContextKeys Y = of("y", Integer.class); + public static final ContextKeys Z = of("z", Integer.class); + public static final ContextKeys WORLD = of("world", String.class); + public static final ContextKeys ID = of("id", String.class); + public static final ContextKeys LOOT = of("loot", LootType.class); + public static final ContextKeys NICK = of("nick", String.class); + public static final ContextKeys OPEN_WATER = of("open_water", Boolean.class); + public static final ContextKeys SIZE = of("size", Float.class); + public static final ContextKeys SIZE_FORMATTED = of("size_formatted", String.class); + public static final ContextKeys PRICE = of("price", Double.class); + public static final ContextKeys PRICE_FORMATTED = of("price_formatted", String.class); + public static final ContextKeys SURROUNDING = of("surrounding", String.class); + public static final ContextKeys TEMP_NEAR_PLAYER = of("near", String.class); + public static final ContextKeys ROD = of("rod", String.class); + public static final ContextKeys BAIT = of("bait", String.class); + public static final ContextKeys HOOK = of("hook", String.class); + public static final ContextKeys IN_BAG = of("in_bag", Boolean.class); + public static final ContextKeys GOAL = of("goal", CompetitionGoal.class); + public static final ContextKeys HOUR = of("hour", String.class); + public static final ContextKeys MINUTE = of("minute", String.class); + public static final ContextKeys SECOND = of("second", String.class); + public static final ContextKeys SECONDS = of("seconds", Integer.class); + public static final ContextKeys PLAYER = of("player", String.class); + public static final ContextKeys SCORE_FORMATTED = of("score_formatted", String.class); + public static final ContextKeys SCORE = of("score", Double.class); + public static final ContextKeys CUSTOM_SCORE = of("custom_score", Double.class); + public static final ContextKeys RANK = of("rank", String.class); + public static final ContextKeys OTHER_LOCATION = of("other_location", Location.class); + public static final ContextKeys OTHER_X = of("other_x", Integer.class); + public static final ContextKeys OTHER_Y = of("other_y", Integer.class); + public static final ContextKeys OTHER_Z = of("other_z", Integer.class); + public static final ContextKeys MONEY = of("money", String.class); + public static final ContextKeys MONEY_FORMATTED = of("money_formatted", String.class); + public static final ContextKeys REST = of("rest", String.class); + public static final ContextKeys REST_FORMATTED = of("rest_formatted", String.class); + public static final ContextKeys SOLD_ITEM_AMOUNT = of("sold_item_amount", Integer.class); + public static final ContextKeys AMOUNT = of("amount", Integer.class); + public static final ContextKeys TOTAL_AMOUNT = of("total_amount", Integer.class); + public static final ContextKeys WEIGHT = of("0", Double.class); + public static final ContextKeys TIME_LEFT = of("time_left", String.class); + public static final ContextKeys PROGRESS = of("progress", String.class); + public static final ContextKeys RECORD = of("record", Float.class); + public static final ContextKeys RECORD_FORMATTED = of("record_formatted", String.class); + public static final ContextKeys CLICKS_LEFT = of("left_clicks", Integer.class); + public static final ContextKeys REQUIRED_TIMES = of("clicks", Integer.class); + + private final String key; + private final Class type; + + protected ContextKeys(String key, Class type) { + this.key = key; + this.type = type; + } + + public String key() { + return key; + } + + public Class type() { + return type; + } + + public static ContextKeys of(String key, Class type) { + return new ContextKeys(key, type); + } + + @Override + public final boolean equals(final Object other) { + if (this == other) { + return true; + } else if (other != null && this.getClass() == other.getClass()) { + ContextKeys that = (ContextKeys) other; + return Objects.equals(this.key, that.key); + } else { + return false; + } + } + + @Override + public final int hashCode() { + return Objects.hashCode(this.key); + } + + @Override + public String toString() { + return "ContextKeys{" + + "key='" + key + '\'' + + '}'; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/PlayerContextImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/PlayerContextImpl.java new file mode 100644 index 00000000..30963aeb --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/PlayerContextImpl.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.context; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * The PlayerContextImpl class implements the Context interface specifically + * for the Player type. It allows for storing and retrieving arguments related + * to a player in the custom fishing mechanics. + */ +public final class PlayerContextImpl implements Context { + + private final Player player; + private final Map, Object> args; + private final Map placeholderMap; + + /** + * Constructs a new PlayerContextImpl with the specified player. + * + * @param player the player to be associated with this context. + */ + public PlayerContextImpl(@Nullable Player player, boolean sync) { + this.player = player; + this.args = sync ? new ConcurrentHashMap<>() : new HashMap<>(); + this.placeholderMap = sync ? new ConcurrentHashMap<>() : new HashMap<>(); + if (player == null) return; + final Location location = player.getLocation(); + arg(ContextKeys.PLAYER, player.getName()) + .arg(ContextKeys.LOCATION, location) + .arg(ContextKeys.X, location.getBlockX()) + .arg(ContextKeys.Y, location.getBlockY()) + .arg(ContextKeys.Z, location.getBlockZ()) + .arg(ContextKeys.WORLD, location.getWorld().getName()); + } + + @Override + public Map, Object> args() { + return args; + } + + @Override + public Map placeholderMap() { + return placeholderMap; + } + + @Override + public PlayerContextImpl arg(ContextKeys key, C value) { + this.args.put(key, value); + this.placeholderMap.put("{" + key.key() + "}", value.toString()); + return this; + } + + @Override + public Context combine(Context other) { + final PlayerContextImpl otherContext = (PlayerContextImpl) other; + this.args.putAll(otherContext.args); + this.placeholderMap.putAll(otherContext.placeholderMap); + return this; + } + + @Override + @SuppressWarnings("unchecked") + public C arg(ContextKeys key) { + return (C) args.get(key); + } + + @Nullable + @SuppressWarnings("unchecked") + @Override + public C remove(ContextKeys key) { + placeholderMap.remove("{" + key.key() + "}"); + return (C) args.remove(key); + } + + @Override + public Player getHolder() { + return player; + } + + @Override + public String toString() { + return "PlayerContext{" + + "args=" + args + + ", player=" + player + + '}'; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/BaseEffect.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/BaseEffect.java deleted file mode 100644 index 53c716a8..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/BaseEffect.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.momirealms.customfishing.api.mechanic.effect; - -import net.momirealms.customfishing.api.mechanic.misc.Value; -import org.bukkit.entity.Player; - -import java.util.Map; - -public class BaseEffect { - - private final Value waitTime; - private final Value waitTimeMultiplier; - private final Value difficulty; - private final Value difficultyMultiplier; - private final Value gameTime; - private final Value gameTimeMultiplier; - - public BaseEffect(Value waitTime, Value waitTimeMultiplier, Value difficulty, Value difficultyMultiplier, Value gameTime, Value gameTimeMultiplier) { - this.waitTime = waitTime; - this.waitTimeMultiplier = waitTimeMultiplier; - this.difficulty = difficulty; - this.difficultyMultiplier = difficultyMultiplier; - this.gameTime = gameTime; - this.gameTimeMultiplier = gameTimeMultiplier; - } - - public Effect build(Player player, Map values) { - return new FishingEffect( - waitTime.get(player, values), - waitTimeMultiplier.get(player, values), - difficulty.get(player, values), - difficultyMultiplier.get(player, values), - gameTime.get(player, values), - gameTimeMultiplier.get(player, values) - ); - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/Effect.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/Effect.java index a63f7e90..43e38e2a 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/Effect.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/Effect.java @@ -17,40 +17,257 @@ package net.momirealms.customfishing.api.mechanic.effect; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.mechanic.misc.WeightModifier; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.util.Pair; +import org.bukkit.entity.Player; import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +/** + * Represents an effect applied in the custom fishing mechanic. + */ public interface Effect { - boolean canLavaFishing(); + /** + * Retrieves the properties of this effect. + * + * @return a map of effect properties and their values + */ + Map, Object> properties(); - double getMultipleLootChance(); + Effect properties(Map, Object> properties); - double getSize(); + /** + * Sets the specified property to the given value. + * + * @param key the property key + * @param value the property value + * @param the type of the property value + * @return the effect instance with the updated property + */ + EffectImpl arg(EffectProperties key, C value); - double getSizeMultiplier(); + /** + * Retrieves the value of the specified property. + * + * @param key the property key + * @param the type of the property value + * @return the value of the specified property + */ + C arg(EffectProperties key); - double getScore(); + /** + * Gets the chance of multiple loots. + * + * @return the multiple loot chance + */ + double multipleLootChance(); - double getScoreMultiplier(); + /** + * Sets the chance of multiple loots. + * + * @param multipleLootChance the new multiple loot chance + * @return the effect instance with the updated multiple loot chance + */ + Effect multipleLootChance(double multipleLootChance); - double getWaitTime(); + /** + * Gets the size adder. + * + * @return the size adder + */ + double sizeAdder(); - double getWaitTimeMultiplier(); + /** + * Sets the size adder. + * + * @param sizeAdder the new size adder + * @return the effect instance with the updated size adder + */ + Effect sizeAdder(double sizeAdder); - double getGameTime(); + /** + * Gets the size multiplier. + * + * @return the size multiplier + */ + double sizeMultiplier(); - double getGameTimeMultiplier(); + /** + * Sets the size multiplier. + * + * @param sizeMultiplier the new size multiplier + * @return the effect instance with the updated size multiplier + */ + Effect sizeMultiplier(double sizeMultiplier); - double getDifficulty(); + /** + * Gets the score adder. + * + * @return the score adder + */ + double scoreAdder(); - double getDifficultyMultiplier(); + /** + * Sets the score adder. + * + * @param scoreAdder the new score adder + * @return the effect instance with the updated score adder + */ + Effect scoreAdder(double scoreAdder); - List> getWeightModifier(); + /** + * Gets the score multiplier. + * + * @return the score multiplier + */ + double scoreMultiplier(); - List> getWeightModifierIgnored(); + /** + * Sets the score multiplier. + * + * @param scoreMultiplier the new score multiplier + * @return the effect instance with the updated score multiplier + */ + Effect scoreMultiplier(double scoreMultiplier); - void merge(Effect effect); + /** + * Gets the wait time adder. + * + * @return the wait time adder + */ + double waitTimeAdder(); + + /** + * Sets the wait time adder. + * + * @param waitTimeAdder the new wait time adder + * @return the effect instance with the updated wait time adder + */ + Effect waitTimeAdder(double waitTimeAdder); + + /** + * Gets the wait time multiplier. + * + * @return the wait time multiplier + */ + double waitTimeMultiplier(); + + /** + * Sets the wait time multiplier. + * + * @param waitTimeMultiplier the new wait time multiplier + * @return the effect instance with the updated wait time multiplier + */ + Effect waitTimeMultiplier(double waitTimeMultiplier); + + /** + * Gets the game time adder. + * + * @return the game time adder + */ + double gameTimeAdder(); + + /** + * Sets the game time adder. + * + * @param gameTimeAdder the new game time adder + * @return the effect instance with the updated game time adder + */ + Effect gameTimeAdder(double gameTimeAdder); + + /** + * Gets the game time multiplier. + * + * @return the game time multiplier + */ + double gameTimeMultiplier(); + + /** + * Sets the game time multiplier. + * + * @param gameTimeMultiplier the new game time multiplier + * @return the effect instance with the updated game time multiplier + */ + Effect gameTimeMultiplier(double gameTimeMultiplier); + + /** + * Gets the difficulty adder. + * + * @return the difficulty adder + */ + double difficultyAdder(); + + /** + * Sets the difficulty adder. + * + * @param difficultyAdder the new difficulty adder + * @return the effect instance with the updated difficulty adder + */ + Effect difficultyAdder(double difficultyAdder); + + /** + * Gets the difficulty multiplier. + * + * @return the difficulty multiplier + */ + double difficultyMultiplier(); + + /** + * Sets the difficulty multiplier. + * + * @param difficultyMultiplier the new difficulty multiplier + * @return the effect instance with the updated difficulty multiplier + */ + Effect difficultyMultiplier(double difficultyMultiplier); + + /** + * Gets the list of weight operations. + * + * @return the list of weight operations + */ + List, Double, Double>>> weightOperations(); + + /** + * Adds the list of weight operations. + * + * @param weightOperations the list of weight operations to add + * @return the effect instance with the updated weight operations + */ + Effect weightOperations(List, Double, Double>>> weightOperations); + + /** + * Gets the list of weight operations that are conditions ignored. + * + * @return the list of weight operations that are conditions ignored + */ + List, Double, Double>>> weightOperationsIgnored(); + + /** + * Adds the list of weight operations that are conditions ignored. + * + * @param weightOperations the list of weight operations that are conditions ignored + * @return the effect instance with the updated ignored weight operations + */ + Effect weightOperationsIgnored(List, Double, Double>>> weightOperations); + + /** + * Combines this effect with another effect. + * + * @param effect the effect to combine with + */ + void combine(Effect effect); + + Effect copy(); + + /** + * Creates a new instance of {@link Effect}. + * + * @return a new {@link Effect} instance + */ + static Effect newInstance() { + return new EffectImpl(); + } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectCarrier.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectCarrier.java deleted file mode 100644 index 103edce2..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectCarrier.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic.effect; - -import net.momirealms.customfishing.api.common.Key; -import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.requirement.Requirement; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; - -public class EffectCarrier { - - private Key key; - private Requirement[] requirements; - private EffectModifier[] effect; - private Map actionMap; - private boolean persist; - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private final EffectCarrier item; - - public Builder() { - this.item = new EffectCarrier(); - } - - public Builder persist(boolean persist) { - item.persist = persist; - return this; - } - - public Builder key(Key key) { - item.key = key; - return this; - } - - public Builder requirements(Requirement[] requirements) { - item.requirements = requirements; - return this; - } - - public Builder effect(EffectModifier[] effect) { - item.effect = effect; - return this; - } - - public Builder actionMap(Map actionMap) { - item.actionMap = actionMap; - return this; - } - - public EffectCarrier build() { - return item; - } - } - - public Key getKey() { - return key; - } - - public Requirement[] getRequirements() { - return requirements; - } - - public EffectModifier[] getEffectModifiers() { - return effect; - } - - public Map getActionMap() { - return actionMap; - } - - @Nullable - public Action[] getActions(ActionTrigger trigger) { - return actionMap.get(trigger); - } - - public boolean isPersist() { - return persist; - } - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public boolean isConditionMet(Condition condition) { - if (requirements == null) return true; - for (Requirement requirement : requirements) { - if (!requirement.isConditionMet(condition)) { - return false; - } - } - return true; - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectImpl.java new file mode 100644 index 00000000..be5ea5c8 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectImpl.java @@ -0,0 +1,234 @@ +package net.momirealms.customfishing.api.mechanic.effect; + +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.util.Pair; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; + +public class EffectImpl implements Effect { + + private final HashMap, Object> properties = new HashMap<>(); + private double multipleLootChance = 0; + private double sizeAdder = 0; + private double sizeMultiplier = 1; + private double scoreAdder = 0; + private double scoreMultiplier = 1; + private double gameTimeAdder = 0; + private double gameTimeMultiplier = 1; + private double waitTimeAdder = 0; + private double waitTimeMultiplier = 1; + private double difficultyAdder = 0; + private double difficultyMultiplier = 1; + private final List, Double, Double>>> weightOperations = new ArrayList<>(); + private final List, Double, Double>>> weightOperationsIgnored = new ArrayList<>(); + + @Override + public Map, Object> properties() { + return properties; + } + + @Override + public Effect properties(Map, Object> properties) { + this.properties.putAll(properties); + return this; + } + + @Override + public EffectImpl arg(EffectProperties key, C value) { + properties.put(key, value); + return this; + } + + @Override + @SuppressWarnings("unchecked") + public C arg(EffectProperties key) { + return (C) properties.get(key); + } + + @Override + public double multipleLootChance() { + return multipleLootChance; + } + + @Override + public Effect multipleLootChance(double multipleLootChance) { + this.multipleLootChance = multipleLootChance; + return this; + } + + @Override + public double sizeAdder() { + return sizeAdder; + } + + @Override + public Effect sizeAdder(double sizeAdder) { + this.sizeAdder = sizeAdder; + return this; + } + + @Override + public double sizeMultiplier() { + return sizeMultiplier; + } + + @Override + public Effect sizeMultiplier(double sizeMultiplier) { + this.sizeMultiplier = sizeMultiplier; + return this; + } + + @Override + public double scoreAdder() { + return scoreAdder; + } + + @Override + public Effect scoreAdder(double scoreAdder) { + this.scoreAdder = scoreAdder; + return this; + } + + @Override + public double scoreMultiplier() { + return scoreMultiplier; + } + + @Override + public Effect scoreMultiplier(double scoreMultiplier) { + this.scoreMultiplier = scoreMultiplier; + return this; + } + + @Override + public double waitTimeAdder() { + return waitTimeAdder; + } + + @Override + public Effect waitTimeAdder(double waitTimeAdder) { + this.waitTimeAdder = waitTimeAdder; + return this; + } + + @Override + public double waitTimeMultiplier() { + return waitTimeMultiplier; + } + + @Override + public Effect waitTimeMultiplier(double waitTimeMultiplier) { + this.waitTimeMultiplier = waitTimeMultiplier; + return this; + } + + @Override + public double gameTimeAdder() { + return gameTimeAdder; + } + + @Override + public Effect gameTimeAdder(double gameTimeAdder) { + this.gameTimeAdder = gameTimeAdder; + return this; + } + + @Override + public double gameTimeMultiplier() { + return gameTimeMultiplier; + } + + @Override + public Effect gameTimeMultiplier(double gameTimeMultiplier) { + this.gameTimeMultiplier = gameTimeMultiplier; + return this; + } + + @Override + public double difficultyAdder() { + return difficultyAdder; + } + + @Override + public Effect difficultyAdder(double difficultyAdder) { + this.difficultyAdder = difficultyAdder; + return this; + } + + @Override + public double difficultyMultiplier() { + return difficultyMultiplier; + } + + @Override + public Effect difficultyMultiplier(double difficultyMultiplier) { + this.difficultyMultiplier = difficultyMultiplier; + return this; + } + + @Override + public List, Double, Double>>> weightOperations() { + return weightOperations; + } + + @Override + public Effect weightOperations(List, Double, Double>>> weightOperations) { + this.weightOperations.addAll(weightOperations); + return this; + } + + @Override + public List, Double, Double>>> weightOperationsIgnored() { + return weightOperationsIgnored; + } + + @Override + public Effect weightOperationsIgnored(List, Double, Double>>> weightOperations) { + this.weightOperationsIgnored.addAll(weightOperations); + return this; + } + + @Override + public void combine(Effect another) { + if (another == null) return; + this.scoreMultiplier += (another.scoreMultiplier() -1); + this.scoreAdder += another.scoreAdder(); + this.sizeMultiplier += (another.sizeMultiplier() -1); + this.sizeAdder += another.sizeAdder(); + this.difficultyMultiplier += (another.difficultyMultiplier() -1); + this.difficultyAdder += another.difficultyAdder(); + this.gameTimeMultiplier += (another.gameTimeMultiplier() - 1); + this.gameTimeAdder += another.gameTimeAdder(); + this.waitTimeMultiplier += (another.waitTimeMultiplier() -1); + this.waitTimeAdder += (another.waitTimeAdder()); + this.multipleLootChance += another.multipleLootChance(); + this.weightOperations.addAll(another.weightOperations()); + this.weightOperationsIgnored.addAll(another.weightOperationsIgnored()); + this.properties.putAll(another.properties()); + } + + @Override + public Effect copy() { + return Effect.newInstance() + .scoreMultiplier(this.scoreMultiplier) + .scoreAdder(this.scoreAdder) + .sizeMultiplier(this.sizeMultiplier) + .sizeAdder(this.sizeAdder) + .difficultyMultiplier(this.difficultyMultiplier) + .difficultyAdder(this.difficultyAdder) + .gameTimeMultiplier(this.gameTimeMultiplier) + .gameTimeAdder(this.gameTimeAdder) + .waitTimeMultiplier(this.waitTimeMultiplier) + .waitTimeAdder(this.waitTimeAdder) + .multipleLootChance(this.multipleLootChance) + .weightOperations(this.weightOperations) + .weightOperationsIgnored(this.weightOperationsIgnored) + .properties(this.properties); + + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectManager.java new file mode 100644 index 00000000..0e2edbfb --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectManager.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.effect; + +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; + +import java.util.Optional; + +public interface EffectManager extends Reloadable { + + boolean registerEffectModifier(EffectModifier effect, MechanicType type); + + Optional getEffectModifier(String id, MechanicType type); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectModifier.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectModifier.java index 6df659cf..df66b481 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectModifier.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectModifier.java @@ -1,25 +1,82 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package net.momirealms.customfishing.api.mechanic.effect; -import net.momirealms.customfishing.api.mechanic.condition.Condition; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.requirement.Requirement; +import net.momirealms.customfishing.common.util.TriConsumer; +import org.bukkit.entity.Player; +import java.util.List; + +/** + * EffectModifier interface for modifying effects in the CustomFishing plugin. + * This interface allows defining conditions and modifications for effects applied to players. + */ public interface EffectModifier { - void modify(FishingEffect effect, Condition condition); + String id(); + + /** + * Returns an array of requirements that must be met by a Player for the effect to be applied. + * + * @return an array of requirements + */ + Requirement[] requirements(); + + /** + * Returns a list of modifiers that apply changes to an effect within a given context. + * + * @return a list of effect modifiers + */ + List, Integer>> modifiers(); + + /** + * Creates and returns a new Builder instance for constructing EffectModifier instances. + * + * @return a new Builder instance + */ + static Builder builder() { + return new EffectModifierImpl.BuilderImpl(); + } + + MechanicType type(); + + /** + * Builder interface for constructing EffectModifier instances. + */ + interface Builder { + + Builder id(String id); + + /** + * Sets the requirements for the EffectModifier being built. + * + * @param requirements a list of requirements + * @return the current Builder instance + */ + Builder requirements(List> requirements); + + /** + * Sets the modifiers for the EffectModifier being built. + * + * @param modifiers a list of effect modifiers + * @return the current Builder instance + */ + Builder modifiers(List, Integer>> modifiers); + + /** + * Set the type of the item + * + * @param type type + * @return the Builder instance. + */ + Builder type(MechanicType type); + + /** + * Builds and returns the EffectModifier instance. + * + * @return the built EffectModifier instance + */ + EffectModifier build(); + } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectModifierImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectModifierImpl.java new file mode 100644 index 00000000..6f0f8ef5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectModifierImpl.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.effect; + +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.requirement.Requirement; +import net.momirealms.customfishing.common.util.TriConsumer; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +public class EffectModifierImpl implements EffectModifier { + + private final Requirement[] requirements; + private final List, Integer>> modifiers; + private final String id; + private final MechanicType type; + + public EffectModifierImpl( + String id, + MechanicType type, + Requirement[] requirements, + List, Integer>> modifiers + ) { + this.requirements = requirements; + this.modifiers = modifiers; + this.id = id; + this.type = type; + } + + @Override + public String id() { + return id; + } + + @Override + public Requirement[] requirements() { + return requirements; + } + + @Override + public List, Integer>> modifiers() { + return modifiers; + } + + @Override + public MechanicType type() { + return type; + } + + public static class BuilderImpl implements Builder { + private final List> requirements = new ArrayList<>(); + private final List, Integer>> modifiers = new ArrayList<>(); + private String id; + private MechanicType type; + @Override + public Builder id(String id) { + this.id = id; + return this; + } + @Override + public Builder requirements(List> requirements) { + this.requirements.addAll(requirements); + return this; + } + @Override + public Builder modifiers(List, Integer>> modifiers) { + this.modifiers.addAll(modifiers); + return this; + } + @Override + public Builder type(MechanicType type) { + this.type = type; + return this; + } + @Override + @SuppressWarnings("unchecked") + public EffectModifier build() { + return new EffectModifierImpl(id, type, this.requirements.toArray(new Requirement[0]), this.modifiers); + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectProperties.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectProperties.java new file mode 100644 index 00000000..2e8d542d --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/EffectProperties.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.effect; + +import java.util.Objects; + +public class EffectProperties { + + public static final EffectProperties LAVA_FISHING = of("lava", Boolean.class); + public static final EffectProperties VOID_FISHING = of("void", Boolean.class); + // It's not actually used because it's a vanilla mechanic + public static final EffectProperties WATER_FISHING = of("water", Boolean.class); + + private final String key; + private final Class type; + + private EffectProperties(String key, Class type) { + this.key = key; + this.type = type; + } + + public String key() { + return key; + } + + public Class type() { + return type; + } + + public static EffectProperties of(String key, Class type) { + return new EffectProperties(key, type); + } + + @Override + public final boolean equals(final Object other) { + if (this == other) { + return true; + } else if (other != null && this.getClass() == other.getClass()) { + EffectProperties that = (EffectProperties) other; + return Objects.equals(this.key, that.key); + } else { + return false; + } + } + + @Override + public final int hashCode() { + return Objects.hashCode(this.key); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/FishingEffect.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/FishingEffect.java deleted file mode 100644 index f8408f48..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/FishingEffect.java +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic.effect; - -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.mechanic.misc.WeightModifier; - -import java.util.ArrayList; -import java.util.List; - -public class FishingEffect implements Effect { - - private boolean lavaFishing = false; - private double multipleLootChance = 0; - private double waitTime = 0; - private double waitTimeMultiplier = 1; - private double size = 0; - private double sizeMultiplier = 1; - private double score = 0; - private double scoreMultiplier = 1; - private double difficulty = 0; - private double difficultyMultiplier = 1; - private double gameTime = 0; - private double gameTimeMultiplier = 1; - - private final List> weightModifier = new ArrayList<>(); - private final List> weightModifierIgnored = new ArrayList<>(); - - public FishingEffect(double waitTime, double waitTimeMultiplier, double difficulty, double difficultyMultiplier, double gameTime, double gameTimeMultiplier) { - this.waitTime = waitTime; - this.waitTimeMultiplier = waitTimeMultiplier; - this.difficulty = difficulty; - this.difficultyMultiplier = difficultyMultiplier; - this.gameTime = gameTime; - this.gameTimeMultiplier = gameTimeMultiplier; - } - - public FishingEffect() { - } - - /** - * Sets whether lava fishing is enabled. - * - * @param lavaFishing True if lava fishing is enabled, false otherwise. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect setLavaFishing(boolean lavaFishing) { - this.lavaFishing = lavaFishing; - return this; - } - - /** - * Sets the multiple loot chance. - * - * @param multipleLootChance The multiple loot chance value to set. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect setMultipleLootChance(double multipleLootChance) { - this.multipleLootChance = multipleLootChance; - return this; - } - - /** - * Sets the size multiplier. - * - * @param sizeMultiplier The size multiplier value to set. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect setSizeMultiplier(double sizeMultiplier) { - this.sizeMultiplier = sizeMultiplier; - return this; - } - - /** - * Sets the size. - * - * @param size The size value to set. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect setSize(double size) { - this.size = size; - return this; - } - - /** - * Sets the score multiplier. - * - * @param scoreMultiplier The score multiplier value to set. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect setScoreMultiplier(double scoreMultiplier) { - this.scoreMultiplier = scoreMultiplier; - return this; - } - - /** - * Sets the score - * - * @param score The score value to set. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect setScore(double score) { - this.score = score; - return this; - } - - /** - * Sets the wait time multiplier. - * - * @param timeMultiplier The wait time multiplier value to set. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect setWaitTimeMultiplier(double timeMultiplier) { - this.waitTimeMultiplier = timeMultiplier; - return this; - } - - /** - * Sets the wait time. - * - * @param waitTime The wait time value to set. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect setWaitTime(double waitTime) { - this.waitTime = waitTime; - return this; - } - - /** - * Sets the difficulty. - * - * @param difficulty The difficulty value to set. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect setDifficulty(double difficulty) { - this.difficulty = difficulty; - return this; - } - - /** - * Sets the difficulty multiplier. - * - * @param difficultyMultiplier The difficulty multiplier value to set. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect setDifficultyMultiplier(double difficultyMultiplier) { - this.difficultyMultiplier = difficultyMultiplier; - return this; - } - - /** - * Sets the game time. - * - * @param gameTime The game time value to set. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect setGameTime(double gameTime) { - this.gameTime = gameTime; - return this; - } - - /** - * Sets the game time multiplier. - * - * @param gameTimeMultiplier The game time multiplier value to set. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect setGameTimeMultiplier(double gameTimeMultiplier) { - this.gameTimeMultiplier = gameTimeMultiplier; - return this; - } - - - /** - * Adds weight modifiers to the FishingEffect. - * - * @param weightModifier A list of pairs representing weight modifiers to add. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect addWeightModifier(List> weightModifier) { - this.weightModifier.addAll(weightModifier); - return this; - } - - /** - * Adds ignored weight modifiers to the FishingEffect. - * - * @param weightModifierIgnored A list of pairs representing ignored weight modifiers to add. - * @return The FishingEffect instance for method chaining. - */ - public FishingEffect addWeightModifierIgnored(List> weightModifierIgnored) { - this.weightModifierIgnored.addAll(weightModifierIgnored); - return this; - } - - /** - * Checks if lava fishing is enabled. - * - * @return True if lava fishing is enabled, false otherwise. - */ - @Override - public boolean canLavaFishing() { - return lavaFishing; - } - - /** - * Retrieves the multiple loot chance. - * - * @return The multiple loot chance value. - */ - @Override - public double getMultipleLootChance() { - return multipleLootChance; - } - - /** - * Retrieves the size multiplier. - * - * @return The size multiplier value. - */ - @Override - public double getSizeMultiplier() { - return sizeMultiplier; - } - - /** - * Retrieves the size. - * - * @return The size value. - */ - @Override - public double getSize() { - return size; - } - - /** - * Retrieves the score multiplier. - * - * @return The score multiplier value. - */ - @Override - public double getScoreMultiplier() { - return scoreMultiplier; - } - - /** - * Retrieves the wait time multiplier. - * - * @return The wait time multiplier value. - */ - @Override - public double getWaitTimeMultiplier() { - return waitTimeMultiplier; - } - - /** - * Retrieves the wait time. - * - * @return The wait time . - */ - @Override - public double getWaitTime() { - return waitTime; - } - - /** - * Retrieves the game time. - * - * @return The game time value. - */ - @Override - public double getGameTime() { - return gameTime; - } - - /** - * Retrieves the game time multiplier. - * - * @return The game time value multiplier. - */ - @Override - public double getGameTimeMultiplier() { - return gameTimeMultiplier; - } - - /** - * Retrieves score modifier. - * - * @return The score value. - */ - @Override - public double getScore() { - return score; - } - - /** - * Retrieves the difficulty. - * - * @return The difficulty value. - */ - @Override - public double getDifficulty() { - return difficulty; - } - - /** - * Retrieves the difficulty multiplier. - * - * @return The difficulty multiplier value. - */ - @Override - public double getDifficultyMultiplier() { - return difficultyMultiplier; - } - - /** - * Retrieves the list of weight modifiers. - * - * @return The list of weight modifiers. - */ - @Override - public List> getWeightModifier() { - return weightModifier; - } - - /** - * Retrieves the list of weight modifiers ignoring conditions. - * - * @return The list of weight modifiers ignoring conditions. - */ - @Override - public List> getWeightModifierIgnored() { - return weightModifierIgnored; - } - - /** - * Merges another Effect into this FishingEffect, combining their properties. - * - * @param another The Effect to merge into this FishingEffect. - */ - @Override - public void merge(Effect another) { - if (another == null) return; - if (another.canLavaFishing()) this.lavaFishing = true; - this.scoreMultiplier += (another.getScoreMultiplier() -1); - this.score += another.getScore(); - this.sizeMultiplier += (another.getSizeMultiplier() -1); - this.size += another.getSize(); - this.difficultyMultiplier += (another.getDifficultyMultiplier() -1); - this.difficulty += another.getDifficulty(); - this.gameTimeMultiplier += (another.getGameTimeMultiplier() - 1); - this.gameTime += another.getGameTime(); - this.waitTimeMultiplier += (another.getWaitTimeMultiplier() -1); - this.multipleLootChance += another.getMultipleLootChance(); - this.weightModifierIgnored.addAll(another.getWeightModifierIgnored()); - this.weightModifier.addAll(another.getWeightModifier()); - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/LootBaseEffect.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/LootBaseEffect.java new file mode 100644 index 00000000..60bb20c7 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/LootBaseEffect.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.effect; + +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import org.bukkit.entity.Player; + +/** + * Represents the base effect applied to loot in the custom fishing mechanic. + */ +public interface LootBaseEffect { + + MathValue DEFAULT_WAIT_TIME_ADDER = MathValue.plain(0); + MathValue DEFAULT_WAIT_TIME_MULTIPLIER = MathValue.plain(1); + MathValue DEFAULT_DIFFICULTY_ADDER = MathValue.plain(0); + MathValue DEFAULT_DIFFICULTY_MULTIPLIER = MathValue.plain(1); + MathValue DEFAULT_GAME_TIME_ADDER = MathValue.plain(0); + MathValue DEFAULT_GAME_TIME_MULTIPLIER = MathValue.plain(1); + + /** + * Gets the adder value for wait time. + * + * @return the wait time adder value + */ + MathValue waitTimeAdder(); + + /** + * Gets the multiplier value for wait time. + * + * @return the wait time multiplier value + */ + MathValue waitTimeMultiplier(); + + /** + * Gets the adder value for difficulty. + * + * @return the difficulty adder value + */ + MathValue difficultyAdder(); + + /** + * Gets the multiplier value for difficulty. + * + * @return the difficulty multiplier value + */ + MathValue difficultyMultiplier(); + + /** + * Gets the adder value for game time. + * + * @return the game time adder value + */ + MathValue gameTimeAdder(); + + /** + * Gets the multiplier value for game time. + * + * @return the game time multiplier value + */ + MathValue gameTimeMultiplier(); + + /** + * Creates a new {@link Builder} instance for constructing {@link LootBaseEffect} objects. + * + * @return a new {@link Builder} instance + */ + static Builder builder() { + return new LootBaseEffectImpl.BuilderImpl(); + } + + Effect toEffect(Context context); + + /** + * Builder interface for constructing {@link LootBaseEffect} instances. + */ + interface Builder { + + /** + * Sets the adder value for wait time. + * + * @param waitTimeAdder the wait time adder value + * @return the builder instance + */ + Builder waitTimeAdder(MathValue waitTimeAdder); + + /** + * Sets the multiplier value for wait time. + * + * @param waitTimeMultiplier the wait time multiplier value + * @return the builder instance + */ + Builder waitTimeMultiplier(MathValue waitTimeMultiplier); + + /** + * Sets the adder value for difficulty. + * + * @param difficultyAdder the difficulty adder value + * @return the builder instance + */ + Builder difficultyAdder(MathValue difficultyAdder); + + /** + * Sets the multiplier value for difficulty. + * + * @param difficultyMultiplier the difficulty multiplier value + * @return the builder instance + */ + Builder difficultyMultiplier(MathValue difficultyMultiplier); + + /** + * Sets the adder value for game time. + * + * @param gameTimeAdder the game time adder value + * @return the builder instance + */ + Builder gameTimeAdder(MathValue gameTimeAdder); + + /** + * Sets the multiplier value for game time. + * + * @param gameTimeMultiplier the game time multiplier value + * @return the builder instance + */ + Builder gameTimeMultiplier(MathValue gameTimeMultiplier); + + /** + * Builds and returns the {@link LootBaseEffect} instance. + * + * @return the built {@link LootBaseEffect} instance + */ + LootBaseEffect build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/LootBaseEffectImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/LootBaseEffectImpl.java new file mode 100644 index 00000000..bbc47ce0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/effect/LootBaseEffectImpl.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.effect; + +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import org.bukkit.entity.Player; + +public class LootBaseEffectImpl implements LootBaseEffect { + + private final MathValue waitTimeAdder; + private final MathValue waitTimeMultiplier; + private final MathValue difficultyAdder; + private final MathValue difficultyMultiplier; + private final MathValue gameTimeAdder; + private final MathValue gameTimeMultiplier; + + public LootBaseEffectImpl( + MathValue waitTimeAdder, + MathValue waitTimeMultiplier, + MathValue difficultyAdder, + MathValue difficultyMultiplier, + MathValue gameTimeAdder, + MathValue gameTimeMultiplier + ) { + this.waitTimeAdder = waitTimeAdder; + this.waitTimeMultiplier = waitTimeMultiplier; + this.difficultyAdder = difficultyAdder; + this.difficultyMultiplier = difficultyMultiplier; + this.gameTimeAdder = gameTimeAdder; + this.gameTimeMultiplier = gameTimeMultiplier; + } + + @Override + public MathValue waitTimeAdder() { + return waitTimeAdder; + } + + @Override + public MathValue waitTimeMultiplier() { + return waitTimeMultiplier; + } + + @Override + public MathValue difficultyAdder() { + return difficultyAdder; + } + + @Override + public MathValue difficultyMultiplier() { + return difficultyMultiplier; + } + + @Override + public MathValue gameTimeAdder() { + return gameTimeAdder; + } + + @Override + public MathValue gameTimeMultiplier() { + return gameTimeMultiplier; + } + + @Override + public Effect toEffect(Context context) { + Effect effect = Effect.newInstance(); + effect.waitTimeAdder(waitTimeAdder.evaluate(context)); + effect.waitTimeMultiplier(waitTimeMultiplier.evaluate(context)); + effect.difficultyAdder(difficultyAdder.evaluate(context)); + effect.difficultyMultiplier(difficultyMultiplier.evaluate(context)); + effect.gameTimeAdder(gameTimeAdder.evaluate(context)); + effect.gameTimeMultiplier(gameTimeMultiplier.evaluate(context)); + return effect; + } + + public static class BuilderImpl implements Builder { + private MathValue waitTimeAdder = DEFAULT_WAIT_TIME_ADDER; + private MathValue waitTimeMultiplier = DEFAULT_WAIT_TIME_MULTIPLIER; + private MathValue difficultyAdder = DEFAULT_DIFFICULTY_ADDER; + private MathValue difficultyMultiplier = DEFAULT_DIFFICULTY_MULTIPLIER; + private MathValue gameTimeAdder = DEFAULT_GAME_TIME_ADDER; + private MathValue gameTimeMultiplier = DEFAULT_GAME_TIME_MULTIPLIER; + @Override + public Builder waitTimeAdder(MathValue waitTimeAdder) { + this.waitTimeAdder = waitTimeAdder; + return this; + } + @Override + public Builder waitTimeMultiplier(MathValue waitTimeMultiplier) { + this.waitTimeMultiplier = waitTimeMultiplier; + return this; + } + @Override + public Builder difficultyAdder(MathValue difficultyAdder) { + this.difficultyAdder = difficultyAdder; + return this; + } + @Override + public Builder difficultyMultiplier(MathValue difficultyMultiplier) { + this.difficultyMultiplier = difficultyMultiplier; + return this; + } + @Override + public Builder gameTimeAdder(MathValue gameTimeAdder) { + this.gameTimeAdder = gameTimeAdder; + return this; + } + @Override + public Builder gameTimeMultiplier(MathValue gameTimeMultiplier) { + this.gameTimeMultiplier = gameTimeMultiplier; + return this; + } + @Override + public LootBaseEffect build() { + return new LootBaseEffectImpl(waitTimeAdder, waitTimeMultiplier, difficultyAdder, difficultyMultiplier, gameTimeAdder, gameTimeMultiplier); + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityConfig.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityConfig.java index 8590a413..0afc2328 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityConfig.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityConfig.java @@ -17,76 +17,109 @@ package net.momirealms.customfishing.api.mechanic.entity; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + import java.util.Map; -public class EntityConfig implements EntitySettings { +/** + * The EntityConfig interface defines the configuration for an entity used in custom fishing mechanics. + * It includes methods to retrieve various properties of the entity such as vectors and an ID, as well + * as a nested Builder interface for constructing instances of EntityConfig. + */ +public interface EntityConfig { - private String entity; - private double horizontalVector; - private double verticalVector; - private Map propertyMap; - private boolean persist; + MathValue DEFAULT_HORIZONTAL_VECTOR = MathValue.plain(1.1); + MathValue DEFAULT_VERTICAL_VECTOR = MathValue.plain(1.2); + String DEFAULT_ENTITY_ID = "COD"; + Map DEFAULT_PROPERTY_MAP = Map.of(); - @Override - public boolean isPersist() { - return persist; + String id(); + + /** + * Retrieves the horizontal vector value for the entity. + * + * @return the horizontal vector value as a double + */ + MathValue horizontalVector(); + + /** + * Retrieves the vertical vector value for the entity. + * + * @return the vertical vector value as a double + */ + MathValue verticalVector(); + + /** + * Retrieves the unique identifier for the entity. + * + * @return the entity ID as a non-null String + */ + @NotNull + String entityID(); + + /** + * Retrieves a map of properties associated with the entity. + * + * @return a non-null map where keys are property names and values are property values + */ + @NotNull + Map propertyMap(); + + /** + * Creates a new Builder instance for constructing an EntityConfig. + * + * @return a new Builder instance + */ + static Builder builder() { + return new EntityConfigImpl.BuilderImpl(); } - @Override - public double getHorizontalVector() { - return horizontalVector; - } + /** + * Builder interface for constructing instances of EntityConfig. + */ + interface Builder { - @Override - public double getVerticalVector() { - return verticalVector; - } + Builder id(String id); - @Override - public String getEntityID() { - return entity; - } + /** + * Sets the entity ID for the EntityConfig being built. + * + * @param value the entity ID as a String + * @return the current Builder instance + */ + Builder entityID(String value); - @Override - public Map getPropertyMap() { - return propertyMap; - } + /** + * Sets the vertical vector value for the EntityConfig being built. + * + * @param value the vertical vector value as a double + * @return the current Builder instance + */ + Builder verticalVector(MathValue value); - public static class Builder { + /** + * Sets the horizontal vector value for the EntityConfig being built. + * + * @param value the horizontal vector value as a double + * @return the current Builder instance + */ + Builder horizontalVector(MathValue value); - private final EntityConfig config; + /** + * Sets the property map for the EntityConfig being built. + * + * @param value a map of properties where keys are property names and values are property values + * @return the current Builder instance + */ + Builder propertyMap(Map value); - public Builder() { - this.config = new EntityConfig(); - } - - public Builder entityID(String value) { - this.config.entity = value; - return this; - } - - public Builder persist(boolean value) { - this.config.persist = value; - return this; - } - - public Builder verticalVector(double value) { - this.config.verticalVector = value; - return this; - } - - public Builder horizontalVector(double value) { - this.config.horizontalVector = value; - return this; - } - - public Builder propertyMap(Map value) { - this.config.propertyMap = value; - return this; - } - - public EntityConfig build() { - return config; - } + /** + * Builds and returns the EntityConfig instance based on the current state of the Builder. + * + * @return a new EntityConfig instance + */ + EntityConfig build(); } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityConfigImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityConfigImpl.java new file mode 100644 index 00000000..d327c601 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityConfigImpl.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.entity; + +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public class EntityConfigImpl implements EntityConfig { + + private final String id; + private final String entityID; + private final MathValue horizontalVector; + private final MathValue verticalVector; + private final Map propertyMap; + + public EntityConfigImpl(String id, String entityID, MathValue horizontalVector, MathValue verticalVector, Map propertyMap) { + this.id = id; + this.entityID = entityID; + this.horizontalVector = horizontalVector; + this.verticalVector = verticalVector; + this.propertyMap = propertyMap; + } + + @Override + public String id() { + return id; + } + + @Override + public MathValue horizontalVector() { + return horizontalVector; + } + + @Override + public MathValue verticalVector() { + return verticalVector; + } + + @NotNull + @Override + public String entityID() { + return entityID; + } + + @NotNull + @Override + public Map propertyMap() { + return propertyMap; + } + + public static class BuilderImpl implements Builder { + private String entity = DEFAULT_ENTITY_ID; + private MathValue horizontalVector = DEFAULT_HORIZONTAL_VECTOR; + private MathValue verticalVector = DEFAULT_VERTICAL_VECTOR; + private Map propertyMap = DEFAULT_PROPERTY_MAP; + private String id; + @Override + public Builder id(String id) { + this.id = id; + return this; + } + @Override + public BuilderImpl entityID(String value) { + this.entity = value; + return this; + } + @Override + public BuilderImpl verticalVector(MathValue value) { + this.verticalVector = value; + return this; + } + @Override + public BuilderImpl horizontalVector(MathValue value) { + this.horizontalVector = value; + return this; + } + @Override + public BuilderImpl propertyMap(Map value) { + this.propertyMap = value; + return this; + } + @Override + public EntityConfigImpl build() { + return new EntityConfigImpl(id, entity, horizontalVector, verticalVector, propertyMap); + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityManager.java new file mode 100644 index 00000000..d56ded96 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityManager.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.entity; + +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +/** + * EntityManager interface for managing custom entities in the fishing plugin. + */ +public interface EntityManager extends Reloadable { + + /** + * Retrieves the configuration for a custom entity by its identifier. + * + * @param id The unique identifier of the entity configuration. + * @return An Optional containing the EntityConfig if found, or an empty Optional if not found. + */ + Optional getEntity(String id); + + boolean registerEntity(EntityConfig entity); + + @NotNull + Entity summonEntityLoot(Context context); +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventCarrier.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventCarrier.java new file mode 100644 index 00000000..ce7fb560 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventCarrier.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.event; + +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; +import net.momirealms.customfishing.api.mechanic.context.Context; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.TreeMap; + +/** + * The EventCarrier interface represents an object that carries events in the custom fishing system. + * It defines methods to trigger actions based on specific triggers and contexts. + */ +public interface EventCarrier { + + /** + * Get the type of item + * + * @return type + */ + MechanicType type(); + + String id(); + + /** + * Whether to disable global actions + * + * @return disable global actions or not + */ + boolean disableGlobalActions(); + + /** + * Triggers actions based on the given context and trigger. + * + * @param context the context of the event, typically containing information about the player involved. + * @param trigger the trigger that activates the actions. + */ + void trigger(Context context, ActionTrigger trigger); + + /** + * Triggers actions based on the given context, trigger, and action occurrence times. + * + * @param context the context of the event, typically containing information about the player involved. + * @param trigger the trigger that activates the actions. + * @param previousTimes the number of times the action has been triggered before. + * @param afterTimes the number of times the action will be triggered after. + */ + void trigger(Context context, ActionTrigger trigger, int previousTimes, int afterTimes); + + /** + * Creates a new Builder instance for constructing EventCarrier objects. + * + * @return a new Builder instance. + */ + static Builder builder() { + return new EventCarrierImpl.BuilderImpl(); + } + + /** + * The Builder interface provides a fluent API for constructing EventCarrier instances. + */ + interface Builder { + + Builder id(String id); + + /** + * Sets the map of actions associated with their triggers. + * + * @param actionMap the map of actions associated with their triggers. + * @return the Builder instance. + */ + Builder actionMap(HashMap[]> actionMap); + + Builder action(ActionTrigger trigger, Action[] actions); + + /** + * Sets the map of actions associated with their triggers and occurrence times. + * + * @param actionTimesMap the map of actions associated with their triggers and occurrence times. + * @return the Builder instance. + */ + Builder actionTimesMap(HashMap[]>> actionTimesMap); + + Builder actionTimes(ActionTrigger trigger, TreeMap[]> actions); + + /** + * Set the type of the item + * + * @param type type + * @return the Builder instance. + */ + Builder type(MechanicType type); + + /** + * Set whether to disable global events + * + * @param value disable or not + * @return the Builder instance. + */ + Builder disableGlobalActions(boolean value); + + /** + * Builds and returns the EventCarrier instance. + * + * @return the constructed EventCarrier instance. + */ + EventCarrier build(); + } +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventCarrierImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventCarrierImpl.java new file mode 100644 index 00000000..3ab0a189 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventCarrierImpl.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.event; + +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.action.ActionManager; +import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; +import net.momirealms.customfishing.api.mechanic.context.Context; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; + +import static java.util.Objects.requireNonNull; + +public class EventCarrierImpl implements EventCarrier { + + private final HashMap[]> actionMap; + private final HashMap[]>> actionTimesMap; + private final MechanicType type; + private final boolean disableGlobalActions; + private final String id; + + public EventCarrierImpl(String id, MechanicType type, boolean disableGlobalActions, HashMap[]> actionMap, HashMap[]>> actionTimesMap) { + this.actionMap = actionMap; + this.actionTimesMap = actionTimesMap; + this.type = type; + this.disableGlobalActions = disableGlobalActions; + this.id = id; + } + + @Override + public MechanicType type() { + return type; + } + + @Override + public String id() { + return id; + } + + @Override + public boolean disableGlobalActions() { + return disableGlobalActions; + } + + @Override + public void trigger(Context context, ActionTrigger trigger) { + Optional.ofNullable(actionMap.get(trigger)).ifPresent(actions -> { + ActionManager.trigger(context, actions); + }); + } + + @Override + public void trigger(Context context, ActionTrigger trigger, int previousTimes, int afterTimes) { + Optional.ofNullable(actionTimesMap.get(trigger)).ifPresent(integerTreeMap -> { + for (Map.Entry[]> entry : integerTreeMap.entrySet()) { + if (entry.getKey() <= previousTimes) + continue; + if (entry.getKey() > afterTimes) + return; + ActionManager.trigger(context, entry.getValue()); + } + }); + } + + public static class BuilderImpl implements Builder { + private final HashMap[]> actionMap = new HashMap<>(); + private final HashMap[]>> actionTimesMap = new HashMap<>(); + private MechanicType type = null; + private boolean disableGlobalActions = false; + private String id; + @Override + public Builder id(String id) { + this.id = id; + return this; + } + @Override + public Builder actionMap(HashMap[]> actionMap) { + this.actionMap.putAll(actionMap); + return this; + } + @Override + public Builder action(ActionTrigger trigger, Action[] actions) { + this.actionMap.put(trigger, actions); + return this; + } + @Override + public Builder actionTimesMap(HashMap[]>> actionTimesMap) { + this.actionTimesMap.putAll(actionTimesMap); + return this; + } + @Override + public Builder actionTimes(ActionTrigger trigger, TreeMap[]> actions) { + this.actionTimesMap.put(trigger, actions); + return this; + } + @Override + public Builder type(MechanicType type) { + this.type = type; + return this; + } + @Override + public Builder disableGlobalActions(boolean value) { + this.disableGlobalActions = value; + return this; + } + @Override + public EventCarrier build() { + return new EventCarrierImpl(requireNonNull(id), requireNonNull(type), disableGlobalActions, actionMap, actionTimesMap); + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventManager.java new file mode 100644 index 00000000..1cb5a711 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventManager.java @@ -0,0 +1,138 @@ +package net.momirealms.customfishing.api.mechanic.event; + +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.action.ActionManager; +import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; + +/** + * EventManager interface for managing events and their associated actions within the custom fishing plugin. + * It provides methods to register, retrieve, and trigger events based on different conditions. + */ +public interface EventManager extends Reloadable { + + /** + * A map storing global actions for different item types and triggers. + */ + Map[]>> GLOBAL_ACTIONS = new HashMap<>(); + + /** + * A map storing global timed actions for different item types and triggers. + */ + Map[]>>> GLOBAL_TIMES_ACTION = new HashMap<>(); + + /** + * Retrieves an EventCarrier by its identifier. + * + * @param id The unique identifier of the event carrier. + * @param type + * @return An Optional containing the EventCarrier if found, or an empty Optional if not found. + */ + Optional getEventCarrier(String id, MechanicType type); + + /** + * Registers a new EventCarrier with a specified identifier. + * + * @param carrier The EventCarrier to be registered. + * @return True if the registration was successful, false otherwise. + */ + boolean registerEventCarrier(EventCarrier carrier); + + /** + * Triggers an event for a given context, identifier, and trigger. + * + * @param context The context in which the event is triggered. + * @param id The unique identifier of the event carrier. + * @param trigger The trigger that initiates the event. + */ + default void trigger(Context context, String id, MechanicType type, ActionTrigger trigger) { + getEventCarrier(id, type).ifPresent(carrier -> trigger(context, carrier, trigger)); + } + + /** + * Triggers an event for a given context, identifier, trigger, and a range of times. + * + * @param context The context in which the event is triggered. + * @param id The unique identifier of the event carrier. + * @param trigger The trigger that initiates the event. + * @param previousTimes The previous times count for the event. + * @param afterTimes The after times count for the event. + */ + default void trigger(Context context, String id, MechanicType type, ActionTrigger trigger, int previousTimes, int afterTimes) { + getEventCarrier(id, type).ifPresent(carrier -> trigger(context, carrier, trigger, previousTimes, afterTimes)); + } + + /** + * Triggers the event actions for a given context and trigger on a specified carrier. + * + * @param context The context in which the event is triggered. + * @param carrier The event carrier. + * @param trigger The trigger that initiates the event. + */ + static void trigger(Context context, EventCarrier carrier, ActionTrigger trigger) { + if (!carrier.disableGlobalActions()) { + triggerGlobalActions(context, carrier.type(), trigger); + } + carrier.trigger(context, trigger); + } + + /** + * Triggers the event actions for a given context, trigger, and times range on a specified carrier. + * + * @param context The context in which the event is triggered. + * @param carrier The event carrier. + * @param trigger The trigger that initiates the event. + * @param previousTimes The previous times count for the event. + * @param afterTimes The after times count for the event. + */ + static void trigger(Context context, EventCarrier carrier, ActionTrigger trigger, int previousTimes, int afterTimes) { + if (!carrier.disableGlobalActions()) { + triggerGlobalActions(context, carrier.type(), trigger, previousTimes, afterTimes); + } + carrier.trigger(context, trigger, previousTimes, afterTimes); + } + + /** + * Triggers global actions for a given context, item type, and trigger. + * + * @param context The context in which the event is triggered. + * @param type The type of item that triggered the event. + * @param trigger The trigger that initiates the event. + */ + static void triggerGlobalActions(Context context, MechanicType type, ActionTrigger trigger) { + Optional.ofNullable(GLOBAL_ACTIONS.get(type)) + .flatMap(actionTriggerMap -> Optional.ofNullable(actionTriggerMap.get(trigger))) + .ifPresent(action -> ActionManager.trigger(context, action)); + } + + /** + * Triggers global timed actions for a given context, item type, trigger, and times range. + * + * @param context The context in which the event is triggered. + * @param type The type of item that triggered the event. + * @param trigger The trigger that initiates the event. + * @param previousTimes The previous times count for the event. + * @param afterTimes The after times count for the event. + */ + static void triggerGlobalActions(Context context, MechanicType type, ActionTrigger trigger, int previousTimes, int afterTimes) { + Optional.ofNullable(GLOBAL_TIMES_ACTION.get(type)) + .flatMap(actionTriggerMap -> Optional.ofNullable(actionTriggerMap.get(trigger))) + .ifPresent(integerTreeMap -> { + for (Map.Entry[]> entry : integerTreeMap.entrySet()) { + if (entry.getKey() <= previousTimes) + continue; + if (entry.getKey() > afterTimes) + return; + ActionManager.trigger(context, entry.getValue()); + } + }); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/BaitAnimationTask.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/BaitAnimationTask.java new file mode 100644 index 00000000..40e1c29a --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/BaitAnimationTask.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.fishing; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import net.momirealms.sparrow.heart.SparrowHeart; +import org.bukkit.entity.FishHook; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.concurrent.TimeUnit; + +public class BaitAnimationTask implements Runnable { + + private final SchedulerTask task; + private final int entityID; + private final Player player; + private final FishHook fishHook; + + public BaitAnimationTask(BukkitCustomFishingPlugin plugin, Player player, FishHook fishHook, ItemStack baitItem) { + this.player = player; + this.fishHook = fishHook; + this.task = plugin.getScheduler().asyncRepeating(this, 50, 50, TimeUnit.MILLISECONDS); + ItemStack itemStack = baitItem.clone(); + itemStack.setAmount(1); + this.entityID = SparrowHeart.getInstance().dropFakeItem(player, itemStack, fishHook.getLocation().clone().subtract(0,0.6,0)); + } + + @Override + public void run() { + SparrowHeart.getInstance().sendClientSideEntityMotion(player, fishHook.getVelocity(), entityID); + SparrowHeart.getInstance().sendClientSideTeleportEntity(player, fishHook.getLocation().clone().subtract(0,0.6,0), false, entityID); + } + + public void cancel() { + task.cancel(); + SparrowHeart.getInstance().removeClientSideEntity(player, entityID); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/CustomFishingHook.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/CustomFishingHook.java new file mode 100644 index 00000000..ea68be0c --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/CustomFishingHook.java @@ -0,0 +1,470 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.fishing; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.event.FishingLootSpawnEvent; +import net.momirealms.customfishing.api.event.FishingResultEvent; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; +import net.momirealms.customfishing.api.mechanic.competition.CompetitionGoal; +import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; +import net.momirealms.customfishing.api.mechanic.fishing.hook.HookMechanic; +import net.momirealms.customfishing.api.mechanic.fishing.hook.LavaFishingMechanic; +import net.momirealms.customfishing.api.mechanic.fishing.hook.VanillaMechanic; +import net.momirealms.customfishing.api.mechanic.fishing.hook.VoidFishingMechanic; +import net.momirealms.customfishing.api.mechanic.game.Game; +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.requirement.RequirementManager; +import net.momirealms.customfishing.api.util.EventUtils; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customfishing.common.util.TriConsumer; +import net.momirealms.customfishing.common.util.TriFunction; +import net.momirealms.sparrow.heart.SparrowHeart; +import net.momirealms.sparrow.heart.feature.inventory.HandSlot; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Statistic; +import org.bukkit.entity.*; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public class CustomFishingHook { + + private final BukkitCustomFishingPlugin plugin; + private final FishHook hook; + private final SchedulerTask task; + private final FishingGears gears; + private final Context context; + private Effect tempFinalEffect; + private HookMechanic hookMechanic; + private Loot nextLoot; + private GamingPlayer gamingPlayer; + private BaitAnimationTask baitAnimationTask; + + private static TriFunction, Effect, List> mechanicProviders = defaultMechanicProviders(); + + public static TriFunction, Effect, List> defaultMechanicProviders() { + return (h, c, e) -> { + ArrayList mechanics = new ArrayList<>(); + mechanics.add(new VanillaMechanic(h, c)); + if (ConfigManager.enableLavaFishing()) mechanics.add(new LavaFishingMechanic(h, e, c)); + if (ConfigManager.enableVoidFishing()) mechanics.add(new VoidFishingMechanic(h, e, c)); + return mechanics; + }; + } + + public static void mechanicProviders(TriFunction, Effect, List> mechanicProviders) { + CustomFishingHook.mechanicProviders = mechanicProviders; + } + + public CustomFishingHook(BukkitCustomFishingPlugin plugin, FishHook hook, FishingGears gears, Context context) { + this.gears = gears; + this.gears.trigger(ActionTrigger.CAST, context); + this.plugin = plugin; + this.hook = hook; + // once it becomes a custom hook, the wait time is controlled by plugin + this.context = context; + Effect effect = Effect.newInstance(); + // The effects impact mechanism at this stage + for (EffectModifier modifier : gears.effectModifiers()) { + for (TriConsumer, Integer> consumer : modifier.modifiers()) { + consumer.accept(effect, context, 0); + } + } + // enable bait animation + if (ConfigManager.baitAnimation() && !gears.getItem(FishingGears.GearType.BAIT).isEmpty()) { + this.baitAnimationTask = new BaitAnimationTask(plugin, context.getHolder(), hook, gears.getItem(FishingGears.GearType.BAIT).stream().findAny().get().right()); + } + + List enabledMechanics = mechanicProviders.apply(hook, context, effect); + this.task = plugin.getScheduler().sync().runRepeating(() -> { + // destroy if hook is invalid + if (!hook.isValid()) { + plugin.getFishingManager().destroy(hook.getOwnerUniqueId()); + return; + } + if (isPlayingGame()) { + return; + } + if (this.hookMechanic != null) { + if (this.hookMechanic.shouldStop()) { + this.hookMechanic.destroy(); + this.hookMechanic = null; + } + } + for (HookMechanic mechanic : enabledMechanics) { + // find the first available mechanic + if (mechanic.canStart()) { + if (this.hookMechanic != mechanic) { + if (this.hookMechanic != null) this.hookMechanic.destroy(); + this.hookMechanic = mechanic; + + // remove bait animation if there exists + if (this.baitAnimationTask != null) { + this.baitAnimationTask.cancel(); + this.baitAnimationTask = null; + } + + // to update some properties + mechanic.preStart(); + Effect tempEffect = effect.copy(); + for (EffectModifier modifier : gears.effectModifiers()) { + for (TriConsumer, Integer> consumer : modifier.modifiers()) { + consumer.accept(tempEffect, context, 1); + } + } + + context.arg(ContextKeys.OTHER_LOCATION, hook.getLocation()); + context.arg(ContextKeys.OTHER_X, hook.getLocation().getBlockX()); + context.arg(ContextKeys.OTHER_Y, hook.getLocation().getBlockY()); + context.arg(ContextKeys.OTHER_Z, hook.getLocation().getBlockZ()); + + // get the next loot + Loot loot = plugin.getLootManager().getNextLoot(effect, context); + if (loot != null) { + this.nextLoot = loot; + + context.arg(ContextKeys.ID, loot.id()); + context.arg(ContextKeys.NICK, loot.nick()); + context.arg(ContextKeys.LOOT, loot.type()); + + plugin.debug("Next loot: " + loot.id()); + // get its basic properties + Effect baseEffect = loot.baseEffect().toEffect(context); + tempEffect.combine(baseEffect); + // apply the gears' effects + for (EffectModifier modifier : gears.effectModifiers()) { + for (TriConsumer, Integer> consumer : modifier.modifiers()) { + consumer.accept(tempEffect, context, 2); + } + } + // start the mechanic + mechanic.start(tempEffect); + + this.tempFinalEffect = tempEffect; + } else { + mechanic.start(tempEffect); + this.tempFinalEffect = tempEffect; + // to prevent players from getting any loot + mechanic.freeze(); + } + } + } + } + }, 1, 1, hook.getLocation()); + } + + public void destroy() { + if (task != null) task.cancel(); + if (hook.isValid()) hook.remove(); + if (hookMechanic != null) hookMechanic.destroy(); + if (gamingPlayer != null) gamingPlayer.destroy(); + if (this.baitAnimationTask != null) { + this.baitAnimationTask.cancel(); + this.baitAnimationTask = null; + } + } + + public Context getContext() { + return context; + } + + @NotNull + public FishHook getHookEntity() { + return hook; + } + + @Nullable + public HookMechanic getCurrentHookMechanic() { + return hookMechanic; + } + + @Nullable + public Loot getNextLoot() { + return nextLoot; + } + + public void onReelIn() { + if (isPlayingGame()) return; + if (hookMechanic != null) { + if (!hookMechanic.isHooked()) { + gears.trigger(ActionTrigger.REEL, context); + end(); + } else { + if (nextLoot.disableGame() || RequirementManager.isSatisfied(context, ConfigManager.skipGameRequirements())) { + handleSuccessfulFishing(); + end(); + } else { + gameStart(); + } + } + } else { + gears.trigger(ActionTrigger.REEL, context); + end(); + } + } + + public void end() { + plugin.getFishingManager().destroy(context.getHolder().getUniqueId()); + } + + public void onBite() { + if (isPlayingGame()) return; + plugin.getEventManager().trigger(context, nextLoot.id(), MechanicType.LOOT, ActionTrigger.BITE); + gears.trigger(ActionTrigger.BITE, context); + if (RequirementManager.isSatisfied(context, ConfigManager.autoFishingRequirements())) { + handleSuccessfulFishing(); + SparrowHeart.getInstance().swingHand(context.getHolder(), gears.getRodSlot()); + end(); + scheduleNextFishing(); + return; + } + if (nextLoot.instantGame()) { + gameStart(); + } + } + + public boolean isPlayingGame() { + return gamingPlayer != null && gamingPlayer.isValid(); + } + + public void cancelCurrentGame() { + if (gamingPlayer == null || !gamingPlayer.isValid()) { + throw new RuntimeException("You can't call this method if the player is not playing the game"); + } + gamingPlayer.cancel(); + gamingPlayer = null; + if (hookMechanic != null) { + hookMechanic.unfreeze(tempFinalEffect); + } + } + + public void gameStart() { + if (isPlayingGame()) + return; + Game nextGame = plugin.getGameManager().getNextGame(tempFinalEffect, context); + if (nextGame != null) { + plugin.debug("Next game: " + nextGame.id()); + gamingPlayer = nextGame.start(this, tempFinalEffect); + if (this.hookMechanic != null) { + this.hookMechanic.freeze(); + } + } else { + plugin.debug("Next game: " + "`null`"); + handleSuccessfulFishing(); + end(); + } + } + + public Optional getGamingPlayer() { + return Optional.ofNullable(gamingPlayer); + } + + private void scheduleNextFishing() { + final Player player = context.getHolder(); + plugin.getScheduler().sync().runLater(() -> { + if (player.isOnline()) { + ItemStack item = player.getInventory().getItem(gears.getRodSlot() == HandSlot.MAIN ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND); + if (item.getType() == Material.FISHING_ROD) { + SparrowHeart.getInstance().useItem(player, gears.getRodSlot(), item); + SparrowHeart.getInstance().swingHand(context.getHolder(), gears.getRodSlot()); + } + } + }, 20, player.getLocation()); + } + + public void onLand() { + gears.trigger(ActionTrigger.LAND, context); + } + + public void onEscape() { + plugin.getEventManager().trigger(context, nextLoot.id(), MechanicType.LOOT, ActionTrigger.ESCAPE); + gears.trigger(ActionTrigger.ESCAPE, context); + } + + public void onLure() { + plugin.getEventManager().trigger(context, nextLoot.id(), MechanicType.LOOT, ActionTrigger.LURE); + gears.trigger(ActionTrigger.LURE, context); + } + + public void handleFailedFishing() { + + // update the hook location + context.arg(ContextKeys.OTHER_LOCATION, hook.getLocation()); + context.arg(ContextKeys.OTHER_X, hook.getLocation().getBlockX()); + context.arg(ContextKeys.OTHER_Y, hook.getLocation().getBlockY()); + context.arg(ContextKeys.OTHER_Z, hook.getLocation().getBlockZ()); + + gears.trigger(ActionTrigger.FAILURE, context); + plugin.getEventManager().trigger(context, nextLoot.id(), MechanicType.LOOT, ActionTrigger.FAILURE); + } + + public void handleSuccessfulFishing() { + + // update the hook location + context.arg(ContextKeys.OTHER_LOCATION, hook.getLocation()); + context.arg(ContextKeys.OTHER_X, hook.getLocation().getBlockX()); + context.arg(ContextKeys.OTHER_Y, hook.getLocation().getBlockY()); + context.arg(ContextKeys.OTHER_Z, hook.getLocation().getBlockZ()); + + LootType lootType = context.arg(ContextKeys.LOOT); + Objects.requireNonNull(lootType, "Missing loot type"); + Objects.requireNonNull(tempFinalEffect, "Missing final effects"); + + int amount; + if (lootType == LootType.ITEM) { + amount = (int) tempFinalEffect.multipleLootChance(); + amount += Math.random() < (tempFinalEffect.multipleLootChance() - amount) ? 2 : 1; + } else { + amount = 1; + } + // set the amount of loot + context.arg(ContextKeys.AMOUNT, amount); + + FishingResultEvent event = new FishingResultEvent(context, FishingResultEvent.Result.SUCCESS, hook, nextLoot); + if (EventUtils.fireAndCheckCancel(event)) { + return; + } + + gears.trigger(ActionTrigger.SUCCESS, context); + + switch (lootType) { + case ITEM -> { + for (int i = 0; i < amount; i++) { + plugin.getScheduler().sync().runLater(() -> { + Item item = plugin.getItemManager().dropItemLoot(context, gears.getItem(FishingGears.GearType.ROD).stream().findAny().orElseThrow().right(), hook); + if (item != null && Objects.equals(context.arg(ContextKeys.NICK), "UNDEFINED")) { + ItemStack stack = item.getItemStack(); + Optional displayName = plugin.getItemManager().wrap(stack).displayName(); + if (displayName.isPresent()) { + context.arg(ContextKeys.NICK, AdventureHelper.jsonToMiniMessage(displayName.get())); + } else { + context.arg(ContextKeys.NICK, ""); + } + } + + FishingLootSpawnEvent spawnEvent = new FishingLootSpawnEvent(context, hook.getLocation(), nextLoot, item); + Bukkit.getPluginManager().callEvent(spawnEvent); + if (item != null && !spawnEvent.summonEntity()) + item.remove(); + if (spawnEvent.skipActions()) + return; + if (item != null && item.isValid() && nextLoot.preventGrabbing()) { + item.getPersistentDataContainer().set(Objects.requireNonNull(NamespacedKey.fromString("owner", plugin.getBoostrap())), PersistentDataType.STRING, context.getHolder().getName()); + } + doSuccessActions(); + }, (long) ConfigManager.multipleLootSpawnDelay() * i, hook.getLocation()); + } + } + case BLOCK -> { + FallingBlock fallingBlock = plugin.getBlockManager().summonBlockLoot(context); + FishingLootSpawnEvent spawnEvent = new FishingLootSpawnEvent(context, hook.getLocation(), nextLoot, fallingBlock); + Bukkit.getPluginManager().callEvent(spawnEvent); + if (!spawnEvent.summonEntity()) + fallingBlock.remove(); + if (spawnEvent.skipActions()) + return; + doSuccessActions(); + } + case ENTITY -> { + Entity entity = plugin.getEntityManager().summonEntityLoot(context); + FishingLootSpawnEvent spawnEvent = new FishingLootSpawnEvent(context, hook.getLocation(), nextLoot, entity); + Bukkit.getPluginManager().callEvent(spawnEvent); + if (!spawnEvent.summonEntity()) + entity.remove(); + if (spawnEvent.skipActions()) + return; + doSuccessActions(); + } + } + } + + private void doSuccessActions() { + FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); + if (competition != null && RequirementManager.isSatisfied(context, competition.getConfig().joinRequirements())) { + Double customScore = context.arg(ContextKeys.CUSTOM_SCORE); + if (customScore != null) { + competition.refreshData(context.getHolder(), customScore); + context.arg(ContextKeys.SCORE_FORMATTED, String.format("%.2f", customScore)); + context.arg(ContextKeys.SCORE, customScore); + } else { + double score = 0; + if (competition.getGoal() == CompetitionGoal.CATCH_AMOUNT) { + score = 1; + competition.refreshData(context.getHolder(), score); + } else if (competition.getGoal() == CompetitionGoal.MAX_SIZE || competition.getGoal() == CompetitionGoal.MIN_SIZE) { + Float size = context.arg(ContextKeys.SIZE); + if (size != null) { + competition.refreshData(context.getHolder(), size); + } + } else if (competition.getGoal() == CompetitionGoal.TOTAL_SCORE) { + score = nextLoot.score().evaluate(context); + if (score != 0) { + competition.refreshData(context.getHolder(), score); + } + } + context.arg(ContextKeys.SCORE_FORMATTED, String.format("%.2f", score)); + context.arg(ContextKeys.SCORE, score); + } + } else { + context.arg(ContextKeys.SCORE_FORMATTED, "0.0"); + context.arg(ContextKeys.SCORE, 0d); + } + + String id = context.arg(ContextKeys.ID); + Player player = context.getHolder(); + + if (!nextLoot.disableStats()) { + plugin.getStorageManager().getOnlineUser(player.getUniqueId()).ifPresent( + userData -> { + userData.statistics().addAmount(nextLoot.statisticKey().amountKey(), 1); + context.arg(ContextKeys.TOTAL_AMOUNT, userData.statistics().getAmount(nextLoot.statisticKey().amountKey())); + Optional.ofNullable(context.arg(ContextKeys.SIZE)).ifPresent(size -> { + float max = Math.max(0, userData.statistics().getMaxSize(nextLoot.statisticKey().sizeKey())); + context.arg(ContextKeys.RECORD, max); + context.arg(ContextKeys.RECORD_FORMATTED, String.format("%.2f", max)); + if (userData.statistics().updateSize(nextLoot.statisticKey().sizeKey(), size)) { + plugin.getEventManager().trigger(context, id, MechanicType.LOOT, ActionTrigger.NEW_SIZE_RECORD); + } + }); + } + ); + } + + plugin.getEventManager().trigger(context, id, MechanicType.LOOT, ActionTrigger.SUCCESS); + player.setStatistic(Statistic.FISH_CAUGHT, player.getStatistic(Statistic.FISH_CAUGHT) + 1); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/FishingGears.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/FishingGears.java new file mode 100644 index 00000000..5c23fd0d --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/FishingGears.java @@ -0,0 +1,425 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.fishing; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ScoreComponent; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; +import net.momirealms.customfishing.api.mechanic.hook.HookConfig; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager; +import net.momirealms.customfishing.api.storage.user.UserData; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import net.momirealms.customfishing.common.item.Item; +import net.momirealms.customfishing.common.util.Pair; +import net.momirealms.customfishing.common.util.TriConsumer; +import net.momirealms.sparrow.heart.feature.inventory.HandSlot; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.BiConsumer; + +public class FishingGears { + + private static final Map, ItemStack>> triggers = new HashMap<>(); + + static { + triggers.put(ActionTrigger.CAST, ((type, context, itemStack) -> type.castFunction.accept(context, itemStack))); + triggers.put(ActionTrigger.REEL, ((type, context, itemStack) -> type.reelFunction.accept(context, itemStack))); + triggers.put(ActionTrigger.LAND, ((type, context, itemStack) -> type.landFunction.accept(context, itemStack))); + triggers.put(ActionTrigger.ESCAPE, ((type, context, itemStack) -> type.escapeFunction.accept(context, itemStack))); + triggers.put(ActionTrigger.LURE, ((type, context, itemStack) -> type.lureFunction.accept(context, itemStack))); + triggers.put(ActionTrigger.SUCCESS, ((type, context, itemStack) -> type.successFunction.accept(context, itemStack))); + triggers.put(ActionTrigger.FAILURE, ((type, context, itemStack) -> type.failureFunction.accept(context, itemStack))); + triggers.put(ActionTrigger.BITE, ((type, context, itemStack) -> type.biteFunction.accept(context, itemStack))); + } + + private static BiConsumer, FishingGears> fishingGearsConsumers = defaultFishingGearsConsumers(); + private final HashMap>> gears = new HashMap<>(); + private final ArrayList modifiers = new ArrayList<>(); + private boolean canFish = true; + private HandSlot rodSlot; + + public static void fishingGearsConsumers(BiConsumer, FishingGears> fishingGearsConsumers) { + FishingGears.fishingGearsConsumers = fishingGearsConsumers; + } + + public FishingGears(Context context) { + fishingGearsConsumers.accept(context, this); + } + + public boolean canFish() { + return canFish; + } + + public void trigger(ActionTrigger trigger, Context context) { + for (Map.Entry>> entry : gears.entrySet()) { + for (Pair itemPair : entry.getValue()) { + BukkitCustomFishingPlugin.getInstance().debug(entry.getKey() + " | " + itemPair.left() + " | " + trigger); + triggers.get(trigger).accept(entry.getKey(), context, itemPair.right()); + BukkitCustomFishingPlugin.getInstance().getEventManager().trigger(context, itemPair.left(), entry.getKey().getType(), trigger); + } + } + } + + @NotNull + public List effectModifiers() { + return modifiers; + } + + public HandSlot getRodSlot() { + return rodSlot; + } + + @NotNull + public Collection> getItem(GearType type) { + return gears.getOrDefault(type, List.of()); + } + + public static BiConsumer, FishingGears> defaultFishingGearsConsumers() { + return (context, fishingGears) -> { + Player player = context.getHolder(); + PlayerInventory playerInventory = player.getInventory(); + ItemStack mainHandItem = playerInventory.getItemInMainHand(); + ItemStack offHandItem = playerInventory.getItemInOffHand(); + // set rod + boolean rodOnMainHand = mainHandItem.getType() == Material.FISHING_ROD; + ItemStack rodItem = rodOnMainHand ? mainHandItem : offHandItem; + String rodID = BukkitCustomFishingPlugin.getInstance().getItemManager().getItemID(rodItem); + fishingGears.gears.put(GearType.ROD, List.of(Pair.of(rodID, rodItem))); + context.arg(ContextKeys.ROD, rodID); + fishingGears.rodSlot = rodOnMainHand ? HandSlot.MAIN : HandSlot.OFF; + BukkitCustomFishingPlugin.getInstance().getEffectManager().getEffectModifier(rodID, MechanicType.ROD).ifPresent(fishingGears.modifiers::add); + + // set enchantments + List> enchants = BukkitCustomFishingPlugin.getInstance().getIntegrationManager().getEnchantments(rodItem); + for (Pair enchantment : enchants) { + String effectID = enchantment.left() + ":" + enchantment.right(); + BukkitCustomFishingPlugin.getInstance().getEffectManager().getEffectModifier(effectID, MechanicType.ENCHANT).ifPresent(fishingGears.modifiers::add); + } + + // set hook + BukkitCustomFishingPlugin.getInstance().getHookManager().getHookID(rodItem).ifPresent(hookID -> { + fishingGears.gears.put(GearType.HOOK, List.of(Pair.of(hookID, rodItem))); + context.arg(ContextKeys.HOOK, hookID); + BukkitCustomFishingPlugin.getInstance().getEffectManager().getEffectModifier(hookID, MechanicType.HOOK).ifPresent(fishingGears.modifiers::add); + }); + + // set bait if it is + boolean hasBait = false; + String anotherItemID = BukkitCustomFishingPlugin.getInstance().getItemManager().getItemID(rodOnMainHand ? offHandItem : mainHandItem); + List type = MechanicType.getTypeByID(anotherItemID); + if (type != null && type.contains(MechanicType.BAIT)) { + fishingGears.gears.put(GearType.BAIT, List.of(Pair.of(anotherItemID, rodOnMainHand ? offHandItem : mainHandItem))); + context.arg(ContextKeys.BAIT, anotherItemID); + BukkitCustomFishingPlugin.getInstance().getEffectManager().getEffectModifier(anotherItemID, MechanicType.BAIT).ifPresent(fishingGears.modifiers::add); + hasBait = true; + } + + // search the bag + if (ConfigManager.enableBag()) { + Optional dataOptional = BukkitCustomFishingPlugin.getInstance().getStorageManager().getOnlineUser(player.getUniqueId()); + if (dataOptional.isPresent()) { + UserData data = dataOptional.get(); + Inventory bag = data.holder().getInventory(); + HashMap uniqueUtils = new HashMap<>(); + for (int i = 0; i < bag.getSize(); i++) { + ItemStack itemInBag = bag.getItem(i); + if (itemInBag == null) continue; + String bagItemID = BukkitCustomFishingPlugin.getInstance().getItemManager().getItemID(itemInBag); + List bagItemType = MechanicType.getTypeByID(bagItemID); + if (bagItemType != null) { + if (!hasBait && bagItemType.contains(MechanicType.BAIT)) { + fishingGears.gears.put(GearType.BAIT, List.of(Pair.of(bagItemID, itemInBag))); + context.arg(ContextKeys.BAIT, bagItemID); + BukkitCustomFishingPlugin.getInstance().getEffectManager().getEffectModifier(bagItemID, MechanicType.BAIT).ifPresent(fishingGears.modifiers::add); + hasBait = true; + } + if (bagItemType.contains(MechanicType.UTIL)) { + uniqueUtils.put(bagItemID, itemInBag); + } + } + } + if (!uniqueUtils.isEmpty()) { + ArrayList> utils = new ArrayList<>(); + for (Map.Entry entry : uniqueUtils.entrySet()) { + utils.add(Pair.of(entry.getKey(), entry.getValue())); + BukkitCustomFishingPlugin.getInstance().getEffectManager().getEffectModifier(entry.getKey(), MechanicType.UTIL).ifPresent(fishingGears.modifiers::add); + } + fishingGears.gears.put(GearType.UTIL, utils); + } + } + } + + // check requirements before checking totems + for (EffectModifier modifier : fishingGears.modifiers) { + if (!RequirementManager.isSatisfied(context, modifier.requirements())) { + fishingGears.canFish = false; + } + } + + // set totems + Collection totemIDs = BukkitCustomFishingPlugin.getInstance().getTotemManager().getActivatedTotems(player.getLocation()); + for (String id : totemIDs) { + BukkitCustomFishingPlugin.getInstance().getEffectManager().getEffectModifier(id, MechanicType.TOTEM).ifPresent(fishingGears.modifiers::add); + } + + // add global effects + fishingGears.modifiers.add( + EffectModifier.builder() + .id("__GLOBAL__") + .modifiers(ConfigManager.globalEffects()) + .build() + ); + }; + } + + public static class GearType { + + public static final GearType ROD = new GearType(MechanicType.ROD, + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> { + if (context.getHolder().getGameMode() != GameMode.CREATIVE) + BukkitCustomFishingPlugin.getInstance().getItemManager().decreaseDurability(context.getHolder(), itemStack, 1, false); + }), + ((context, itemStack) -> { + if (context.getHolder().getGameMode() != GameMode.CREATIVE) + BukkitCustomFishingPlugin.getInstance().getItemManager().decreaseDurability(context.getHolder(), itemStack, 1, false); + }), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}) + ); + + public static final GearType BAIT = new GearType(MechanicType.BAIT, + ((context, itemStack) -> { + if (context.getHolder().getGameMode() != GameMode.CREATIVE) + itemStack.setAmount(itemStack.getAmount() - 1); + }), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}) + ); + + public static final GearType HOOK = new GearType(MechanicType.HOOK, + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> { + if (context.getHolder().getGameMode() != GameMode.CREATIVE) { + Item wrapped = BukkitCustomFishingPlugin.getInstance().getItemManager().wrap(itemStack.clone()); + String hookID = (String) wrapped.getTag("CustomFishing", "hook_id").orElseThrow(() -> new RuntimeException("This error should never occur")); + wrapped.getTag("CustomFishing", "hook_max_damage").ifPresent(max -> { + int maxDamage = (int) max; + int hookDamage = (int) wrapped.getTag("CustomFishing", "hook_damage").orElse(0) + 1; + if (hookDamage >= maxDamage) { + wrapped.removeTag("CustomFishing", "hook_damage"); + wrapped.removeTag("CustomFishing", "hook_id"); + wrapped.removeTag("CustomFishing", "hook_stack"); + wrapped.removeTag("CustomFishing", "hook_max_damage"); + BukkitCustomFishingPlugin.getInstance().getSenderFactory().getAudience(context.getHolder()).playSound(Sound.sound(Key.key("minecraft:entity.item.break"), Sound.Source.PLAYER, 1, 1)); + } else { + wrapped.setTag(hookDamage, "CustomFishing", "hook_damage"); + HookConfig hookConfig = BukkitCustomFishingPlugin.getInstance().getHookManager().getHook(hookID).orElseThrow(); + List previousLore = wrapped.lore().orElse(new ArrayList<>()); + List newLore = new ArrayList<>(); + List durabilityLore = new ArrayList<>(); + for (String previous : previousLore) { + Component component = AdventureHelper.jsonToComponent(previous); + if (component instanceof ScoreComponent scoreComponent && scoreComponent.name().equals("cf")) { + if (scoreComponent.objective().equals("hook")) { + continue; + } else if (scoreComponent.objective().equals("durability")) { + durabilityLore.add(previous); + continue; + } + } + newLore.add(previous); + } + for (String lore : hookConfig.lore()) { + ScoreComponent.Builder builder = Component.score().name("cf").objective("hook"); + builder.append(AdventureHelper.miniMessage(lore.replace("{dur}", String.valueOf(maxDamage - hookDamage)).replace("{max}", String.valueOf(maxDamage)))); + newLore.add(AdventureHelper.componentToJson(builder.build())); + } + newLore.addAll(durabilityLore); + wrapped.lore(newLore); + } + itemStack.setItemMeta(wrapped.load().getItemMeta()); + }); + } + }), + ((context, itemStack) -> { + if (context.getHolder().getGameMode() != GameMode.CREATIVE) { + Item wrapped = BukkitCustomFishingPlugin.getInstance().getItemManager().wrap(itemStack.clone()); + String hookID = (String) wrapped.getTag("CustomFishing", "hook_id").orElseThrow(() -> new RuntimeException("This error should never occur")); + wrapped.getTag("CustomFishing", "hook_max_damage").ifPresent(max -> { + int maxDamage = (int) max; + int hookDamage = (int) wrapped.getTag("CustomFishing", "hook_damage").orElse(0) + 1; + if (hookDamage >= maxDamage) { + wrapped.removeTag("CustomFishing", "hook_damage"); + wrapped.removeTag("CustomFishing", "hook_id"); + wrapped.removeTag("CustomFishing", "hook_stack"); + wrapped.removeTag("CustomFishing", "hook_max_damage"); + BukkitCustomFishingPlugin.getInstance().getSenderFactory().getAudience(context.getHolder()).playSound(Sound.sound(Key.key("minecraft:entity.item.break"), Sound.Source.PLAYER, 1, 1)); + } else { + wrapped.setTag(hookDamage, "CustomFishing", "hook_damage"); + HookConfig hookConfig = BukkitCustomFishingPlugin.getInstance().getHookManager().getHook(hookID).orElseThrow(); + List previousLore = wrapped.lore().orElse(new ArrayList<>()); + List newLore = new ArrayList<>(); + List durabilityLore = new ArrayList<>(); + for (String previous : previousLore) { + Component component = AdventureHelper.jsonToComponent(previous); + if (component instanceof ScoreComponent scoreComponent && scoreComponent.name().equals("cf")) { + if (scoreComponent.objective().equals("hook")) { + continue; + } else if (scoreComponent.objective().equals("durability")) { + durabilityLore.add(previous); + continue; + } + } + newLore.add(previous); + } + for (String lore : hookConfig.lore()) { + ScoreComponent.Builder builder = Component.score().name("cf").objective("hook"); + builder.append(AdventureHelper.miniMessage(lore.replace("{dur}", String.valueOf(maxDamage - hookDamage)).replace("{max}", String.valueOf(maxDamage)))); + newLore.add(AdventureHelper.componentToJson(builder.build())); + } + newLore.addAll(durabilityLore); + wrapped.lore(newLore); + } + itemStack.setItemMeta(wrapped.load().getItemMeta()); + }); + } + }), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}) + ); + + public static final GearType UTIL = new GearType(MechanicType.UTIL, + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}), + ((context, itemStack) -> {}) + ); + + private final MechanicType type; + private BiConsumer, ItemStack> castFunction; + private BiConsumer, ItemStack> reelFunction; + private BiConsumer, ItemStack> biteFunction; + private BiConsumer, ItemStack> successFunction; + private BiConsumer, ItemStack> failureFunction; + private BiConsumer, ItemStack> lureFunction; + private BiConsumer, ItemStack> escapeFunction; + private BiConsumer, ItemStack> landFunction; + + public GearType(MechanicType type, + BiConsumer, ItemStack> castFunction, BiConsumer, ItemStack> reelFunction, + BiConsumer, ItemStack> biteFunction, BiConsumer, ItemStack> successFunction, + BiConsumer, ItemStack> failureFunction, BiConsumer, ItemStack> lureFunction, + BiConsumer, ItemStack> escapeFunction, BiConsumer, ItemStack> landFunction + ) { + this.type = type; + this.castFunction = castFunction; + this.reelFunction = reelFunction; + this.biteFunction = biteFunction; + this.successFunction = successFunction; + this.failureFunction = failureFunction; + this.landFunction = landFunction; + this.lureFunction = lureFunction; + this.escapeFunction = escapeFunction; + } + + public void castFunction(BiConsumer, ItemStack> castFunction) { + this.castFunction = castFunction; + } + + public void reelFunction(BiConsumer, ItemStack> reelFunction) { + this.reelFunction = reelFunction; + } + + public void biteFunction(BiConsumer, ItemStack> biteFunction) { + this.biteFunction = biteFunction; + } + + public void successFunction(BiConsumer, ItemStack> successFunction) { + this.successFunction = successFunction; + } + + public void failureFunction(BiConsumer, ItemStack> failureFunction) { + this.failureFunction = failureFunction; + } + + public void escapeFunction(BiConsumer, ItemStack> escapeFunction) { + this.escapeFunction = escapeFunction; + } + + public void lureFunction(BiConsumer, ItemStack> lureFunction) { + this.lureFunction = lureFunction; + } + + public void landFunction(BiConsumer, ItemStack> landFunction) { + this.landFunction = landFunction; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + GearType gearType = (GearType) object; + return Objects.equals(type, gearType.type); + } + + @Override + public int hashCode() { + return Objects.hashCode(type); + } + + @Override + public String toString() { + return type.toString(); + } + + public MechanicType getType() { + return type; + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/data/LegacyDataStorageInterface.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/FishingManager.java similarity index 62% rename from api/src/main/java/net/momirealms/customfishing/api/data/LegacyDataStorageInterface.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/FishingManager.java index 190206bc..803a3860 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/data/LegacyDataStorageInterface.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/FishingManager.java @@ -15,19 +15,22 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.data; +package net.momirealms.customfishing.api.mechanic.fishing; + +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.bukkit.entity.FishHook; +import org.bukkit.entity.Player; import java.util.Optional; import java.util.UUID; -import java.util.concurrent.CompletableFuture; -public interface LegacyDataStorageInterface extends DataStorageInterface { +public interface FishingManager extends Reloadable { - /** - * Retrieve legacy player data from the SQL database. - * - * @param uuid The UUID of the player. - * @return A CompletableFuture containing the optional legacy player data. - */ - CompletableFuture> getLegacyPlayerData(UUID uuid); + Optional getFishHook(Player player); + + Optional getFishHook(UUID player); + + Optional getOwner(FishHook hook); + + void destroy(UUID player); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/Icon.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/FishingPreparation.java similarity index 88% rename from plugin/src/main/java/net/momirealms/customfishing/gui/Icon.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/FishingPreparation.java index 7124f185..1b9825d3 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/Icon.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/FishingPreparation.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.gui; +package net.momirealms.customfishing.api.mechanic.fishing; -public interface Icon { +public class FishingPreparation { } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityLibrary.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/hook/HookMechanic.java similarity index 65% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityLibrary.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/hook/HookMechanic.java index 74f4358e..3b0c367b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityLibrary.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/hook/HookMechanic.java @@ -15,16 +15,25 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.mechanic.entity; +package net.momirealms.customfishing.api.mechanic.fishing.hook; -import org.bukkit.Location; -import org.bukkit.entity.Entity; +import net.momirealms.customfishing.api.mechanic.effect.Effect; -import java.util.Map; +public interface HookMechanic +{ + boolean canStart(); -public interface EntityLibrary { + boolean shouldStop(); - String identification(); + void preStart(); - Entity spawn(Location location, String id, Map propertyMap); + void start(Effect finalEffect); + + boolean isHooked(); + + void destroy(); + + void freeze(); + + void unfreeze(Effect finalEffect); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/hook/LavaFishingMechanic.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/hook/LavaFishingMechanic.java new file mode 100644 index 00000000..543dd7c9 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/hook/LavaFishingMechanic.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.fishing.hook; + +import io.papermc.paper.block.fluid.FluidData; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.event.FishingHookStateEvent; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.effect.EffectProperties; +import net.momirealms.customfishing.api.util.EventUtils; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customfishing.common.util.RandomUtils; +import org.bukkit.*; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.FishHook; +import org.bukkit.entity.Player; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.util.Vector; + +import java.util.Objects; +import java.util.concurrent.ThreadLocalRandom; + +public class LavaFishingMechanic implements HookMechanic { + + private final FishHook hook; + private final Effect gearsEffect; + private final Context context; + private ArmorStand tempEntity; + private SchedulerTask task; + private int timeUntilLured; + private int timeUntilHooked; + private int nibble; + private boolean hooked; + private float fishAngle; + private int currentState; + private int jumpTimer; + private boolean firstTime = true; + private boolean freeze = false; + + public LavaFishingMechanic(FishHook hook, Effect gearsEffect, Context context) { + this.hook = hook; + this.gearsEffect = gearsEffect; + this.context = context; + } + + @Override + public boolean canStart() { + if (!(boolean) gearsEffect.properties().getOrDefault(EffectProperties.LAVA_FISHING, false)) { + return false; + } + if (hook.isInLava()) { + return true; + } + float lavaHeight = 0F; + FluidData fluidData = this.hook.getWorld().getFluidData(this.hook.getLocation()); + if (fluidData.getFluidType() == Fluid.LAVA || fluidData.getFluidType() == Fluid.FLOWING_LAVA) { + lavaHeight = (float) (fluidData.getLevel() * 0.125); + } + return lavaHeight > 0 && this.hook.getY() % 1 <= lavaHeight; + } + + @Override + public boolean shouldStop() { + if (hook.isInLava()) { + return false; + } + return hook.isOnGround() || (hook.getLocation().getBlock().getType() != Material.LAVA && hook.getLocation().getBlock().getRelative(BlockFace.DOWN).getType() != Material.LAVA); + } + + @Override + public void preStart() { + this.context.arg(ContextKeys.SURROUNDING, EffectProperties.LAVA_FISHING.key()); + } + + @Override + public void start(Effect finalEffect) { + EventUtils.fireAndForget(new FishingHookStateEvent(context.getHolder(), hook, FishingHookStateEvent.State.LAND)); + this.setWaitTime(finalEffect); + this.task = BukkitCustomFishingPlugin.getInstance().getScheduler().sync().runRepeating(() -> { + float lavaHeight = 0F; + FluidData fluidData = this.hook.getWorld().getFluidData(this.hook.getLocation()); + if (fluidData.getFluidType() == Fluid.LAVA || fluidData.getFluidType() == Fluid.FLOWING_LAVA) { + lavaHeight = (float) (fluidData.getLevel() * 0.125); + } + if (this.nibble > 0) { + --this.nibble; + if (this.hook.getY() % 1 <= lavaHeight) { + this.jumpTimer++; + if (this.jumpTimer >= 4) { + this.jumpTimer = 0; + this.hook.setVelocity(new Vector(0,0.24,0)); + } + } + if (this.nibble <= 0) { + this.timeUntilLured = 0; + this.timeUntilHooked = 0; + this.hooked = false; + this.jumpTimer = 0; + this.currentState = 0; + } + } else { + if (this.hook.getY() % 1 <= lavaHeight || this.hook.isInLava()) { + Vector previousVector = this.hook.getVelocity(); + this.hook.setVelocity(new Vector(previousVector.getX() * 0.6, Math.min(0.1, Math.max(-0.1, previousVector.getY() + 0.1)), previousVector.getZ() * 0.6)); + this.currentState = 1; + } else { + if (currentState == 1) { + this.currentState = 0; + // set temp entity + this.tempEntity = this.hook.getWorld().spawn(this.hook.getLocation().clone().subtract(0,1,0), ArmorStand.class); + this.setTempEntityProperties(this.tempEntity); + this.hook.setHookedEntity(this.tempEntity); + if (!firstTime) { + EventUtils.fireAndForget(new FishingHookStateEvent(context.getHolder(), hook, FishingHookStateEvent.State.ESCAPE)); + } + firstTime = false; + } + } + float f; + float f1; + float f2; + double d0; + double d1; + double d2; + if (this.timeUntilHooked > 0) { + this.timeUntilHooked -= 1; + if (this.timeUntilHooked > 0) { + this.fishAngle += (float) RandomUtils.triangle(0.0D, 9.188D); + f = this.fishAngle * 0.017453292F; + f1 = (float) Math.sin(f); + f2 = (float) Math.cos(f); + d0 = hook.getX() + (double) (f1 * (float) this.timeUntilHooked * 0.1F); + d1 = hook.getY(); + d2 = hook.getZ() + (double) (f2 * (float) this.timeUntilHooked * 0.1F); + if (RandomUtils.generateRandomFloat(0,1) < 0.15F) { + hook.getWorld().spawnParticle(Particle.FLAME, d0, d1 - 0.10000000149011612D, d2, 1, f1, 0.1D, f2, 0.0D); + } + float f3 = f1 * 0.04F; + float f4 = f2 * 0.04F; + hook.getWorld().spawnParticle(Particle.FLAME, d0, d1, d2, 0, f4, 0.01D, -f3, 1.0D); + } else { + double d3 = hook.getY() + 0.5D; + hook.getWorld().spawnParticle(Particle.FLAME, hook.getX(), d3, hook.getZ(), (int) (1.0F + 0.3 * 20.0F), 0.3, 0.0D, 0.3, 0.20000000298023224D); + this.nibble = RandomUtils.generateRandomInt(20, 40); + this.hooked = true; + hook.getWorld().playSound(hook.getLocation(), Sound.ENTITY_GENERIC_EXTINGUISH_FIRE, 0.25F, 1.0F + (RandomUtils.generateRandomFloat(0,1)-RandomUtils.generateRandomFloat(0,1)) * 0.4F); + EventUtils.fireAndForget(new FishingHookStateEvent(context.getHolder(), hook, FishingHookStateEvent.State.BITE)); + if (this.tempEntity != null && this.tempEntity.isValid()) { + this.tempEntity.remove(); + } + } + } else if (timeUntilLured > 0) { + if (!freeze) { + timeUntilLured--; + } + if (this.timeUntilLured <= 0) { + this.fishAngle = RandomUtils.generateRandomFloat(0F, 360F); + this.timeUntilHooked = RandomUtils.generateRandomInt(20, 80); + EventUtils.fireAndForget(new FishingHookStateEvent(context.getHolder(), hook, FishingHookStateEvent.State.LURE)); + } + } else { + setWaitTime(finalEffect); + } + } + }, 1, 1, hook.getLocation()); + } + + @Override + public boolean isHooked() { + return hooked; + } + + @Override + public void destroy() { + if (this.tempEntity != null && this.tempEntity.isValid()) { + this.tempEntity.remove(); + } + if (this.task != null) { + this.task.cancel(); + } + freeze = false; + } + + @Override + public void freeze() { + freeze = true; + } + + @Override + public void unfreeze(Effect effect) { + freeze = false; + } + + private void setWaitTime(Effect effect) { + int before = ThreadLocalRandom.current().nextInt(ConfigManager.lavaMaxTime() - ConfigManager.lavaMinTime() + 1) + ConfigManager.lavaMinTime(); + int after = Math.max(ConfigManager.lavaMinTime(), (int) (before * effect.waitTimeMultiplier() + effect.waitTimeAdder())); + BukkitCustomFishingPlugin.getInstance().debug("Wait time: " + before + " -> " + after + " ticks"); + this.timeUntilLured = after; + } + + private void setTempEntityProperties(ArmorStand entity) { + entity.setInvisible(true); + entity.setCollidable(false); + entity.setInvulnerable(true); + entity.setVisible(false); + entity.setCustomNameVisible(false); + entity.setSmall(true); + entity.setGravity(false); + entity.getPersistentDataContainer().set( + Objects.requireNonNull(NamespacedKey.fromString("temp-entity", BukkitCustomFishingPlugin.getInstance().getBoostrap())), + PersistentDataType.STRING, + "lava" + ); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/hook/VanillaMechanic.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/hook/VanillaMechanic.java new file mode 100644 index 00000000..e3ff638a --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/hook/VanillaMechanic.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.fishing.hook; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.event.FishingHookStateEvent; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.effect.EffectProperties; +import net.momirealms.customfishing.api.util.EventUtils; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import net.momirealms.sparrow.heart.SparrowHeart; +import org.bukkit.entity.FishHook; +import org.bukkit.entity.Player; + +import java.util.concurrent.ThreadLocalRandom; + +public class VanillaMechanic implements HookMechanic { + + private final FishHook hook; + private final Context context; + private SchedulerTask task; + private boolean isHooked = false; + private int tempWaitTime; + + public VanillaMechanic(FishHook hook, Context context) { + this.hook = hook; + this.context = context; + } + + @Override + public boolean canStart() { + return hook.isInWater(); + } + + @Override + public boolean shouldStop() { + return hook.getState() != FishHook.HookState.BOBBING; + } + + @Override + public void preStart() { + this.context.arg(ContextKeys.SURROUNDING, EffectProperties.WATER_FISHING.key()); + } + + @Override + public void start(Effect finalEffect) { + EventUtils.fireAndForget(new FishingHookStateEvent(context.getHolder(), hook, FishingHookStateEvent.State.LAND)); + setWaitTime(hook, finalEffect); + this.task = BukkitCustomFishingPlugin.getInstance().getScheduler().sync().runRepeating(() -> { + if (isHooked) { + if (!isHooked()) { + isHooked = false; + setWaitTime(hook, finalEffect); + } + } else { + if (isHooked()) { + isHooked = true; + } + } + }, 1, 1, hook.getLocation()); + } + + private void setWaitTime(FishHook hook, Effect effect) { + BukkitCustomFishingPlugin.getInstance().getScheduler().sync().runLater(() -> { + if (!ConfigManager.overrideVanillaWaitTime()) { + int before = Math.max(hook.getWaitTime(), 0); + int after = (int) Math.max(100, before * effect.waitTimeMultiplier() + effect.waitTimeAdder()); + BukkitCustomFishingPlugin.getInstance().debug("Wait time: " + before + " -> " + after + " ticks"); + hook.setWaitTime(after); + } else { + int before = ThreadLocalRandom.current().nextInt(ConfigManager.waterMaxTime() - ConfigManager.waterMinTime() + 1) + ConfigManager.waterMinTime(); + int after = Math.max(ConfigManager.waterMinTime(), (int) (before * effect.waitTimeMultiplier() + effect.waitTimeAdder())); + hook.setWaitTime(after); + BukkitCustomFishingPlugin.getInstance().debug("Wait time: " + before + " -> " + after + " ticks"); + } + }, 1, hook.getLocation()); + } + + @Override + public boolean isHooked() { + return SparrowHeart.getInstance().isFishingHookBit(hook); + } + + @Override + public void destroy() { + if (this.task != null) this.task.cancel(); + this.tempWaitTime = 0; + } + + @Override + public void freeze() { + if (hook.getWaitTime() > 0) { + this.tempWaitTime = hook.getWaitTime(); + } + hook.setWaitTime(Integer.MAX_VALUE); + } + + @Override + public void unfreeze(Effect effect) { + if (this.tempWaitTime != 0) { + hook.setWaitTime(this.tempWaitTime); + this.tempWaitTime = 0; + } else { + setWaitTime(hook, effect); + } + } + + public void onBite() { + EventUtils.fireAndForget(new FishingHookStateEvent(context.getHolder(), hook, FishingHookStateEvent.State.BITE)); + } + + public void onFailedAttempt() { + EventUtils.fireAndForget(new FishingHookStateEvent(context.getHolder(), hook, FishingHookStateEvent.State.ESCAPE)); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/hook/VoidFishingMechanic.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/hook/VoidFishingMechanic.java new file mode 100644 index 00000000..fb5b6e8b --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/hook/VoidFishingMechanic.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.fishing.hook; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.event.FishingHookStateEvent; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.effect.EffectProperties; +import net.momirealms.customfishing.api.util.EventUtils; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customfishing.common.util.RandomUtils; +import org.bukkit.NamespacedKey; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.FishHook; +import org.bukkit.entity.Player; +import org.bukkit.persistence.PersistentDataType; + +import java.util.Objects; +import java.util.concurrent.ThreadLocalRandom; + +public class VoidFishingMechanic implements HookMechanic { + + private final FishHook hook; + private final Effect gearsEffect; + private final Context context; + private ArmorStand tempEntity; + private SchedulerTask task; + private int timeUntilLured; + private int timeUntilHooked; + private int nibble; + private boolean hooked; + private float fishAngle; + private int timer; + private boolean freeze; + + public VoidFishingMechanic(FishHook hook, Effect gearsEffect, Context context) { + this.hook = hook; + this.gearsEffect = gearsEffect; + this.context = context; + } + + @Override + public boolean canStart() { + if (!(boolean) gearsEffect.properties().getOrDefault(EffectProperties.VOID_FISHING, false)) { + return false; + } + return hook.getLocation().getY() <= hook.getWorld().getMinHeight(); + } + + @Override + public boolean shouldStop() { + return hook.getLocation().getY() > hook.getWorld().getMinHeight(); + } + + @Override + public void preStart() { + this.context.arg(ContextKeys.SURROUNDING, EffectProperties.VOID_FISHING.key()); + } + + @Override + public void start(Effect finalEffect) { + EventUtils.fireAndForget(new FishingHookStateEvent(context.getHolder(), hook, FishingHookStateEvent.State.LAND)); + this.setWaitTime(finalEffect); + this.tempEntity = hook.getWorld().spawn(hook.getLocation().clone().subtract(0,1,0), ArmorStand.class); + this.setTempEntityProperties(this.tempEntity); + this.hook.setHookedEntity(this.tempEntity); + this.task = BukkitCustomFishingPlugin.getInstance().getScheduler().sync().runRepeating(() -> { + timer++; + if (timer % 2 == 0) { + if (timer >= 16) timer = 0; + hook.getWorld().spawnParticle(Particle.END_ROD, hook.getX() + 0.5 * Math.cos(timer * 22.5D * 0.017453292F), hook.getY() - 0.15, hook.getZ() + 0.5 * Math.sin(timer * 22.5D * 0.017453292F), 0,0,0,0); + } + if (this.nibble > 0) { + --this.nibble; + if (this.nibble % 4 == 0) { + if (RandomUtils.generateRandomDouble(0, 1) < 0.5) { + hook.getWorld().spawnParticle(Particle.END_ROD, hook.getX(), hook.getY(), hook.getZ(), (int) (1.0F + 0.3 * 20.0F), 0.3, 0.0D, 0.3, 0.10000000298023224D); + } else { + hook.getWorld().spawnParticle(Particle.DRAGON_BREATH, hook.getX(), hook.getY(), hook.getZ(), (int) (1.0F + 0.3 * 20.0F), 0.3, 0.0D, 0.3, 0.10000000298023224D); + } + } + if (this.nibble <= 0) { + this.timeUntilLured = 0; + this.timeUntilHooked = 0; + this.hooked = false; + EventUtils.fireAndForget(new FishingHookStateEvent(context.getHolder(), hook, FishingHookStateEvent.State.ESCAPE)); + } + } else { + float f; + float f1; + float f2; + double d0; + double d1; + double d2; + if (this.timeUntilHooked > 0) { + this.timeUntilHooked -= 1; + if (this.timeUntilHooked > 0) { + this.fishAngle += (float) RandomUtils.triangle(0.0D, 9.188D); + f = this.fishAngle * 0.017453292F; + f1 = (float) Math.sin(f); + f2 = (float) Math.cos(f); + d0 = hook.getX() + (double) (f1 * (float) this.timeUntilHooked * 0.1F); + d1 = hook.getY(); + d2 = hook.getZ() + (double) (f2 * (float) this.timeUntilHooked * 0.1F); + if (RandomUtils.generateRandomFloat(0,1) < 0.15F) { + hook.getWorld().spawnParticle(Particle.END_ROD, d0, d1 - 0.10000000149011612D, d2, 1, f1, 0.1D, f2, 0.0D); + } + float f3 = f1 * 0.04F; + float f4 = f2 * 0.04F; + hook.getWorld().spawnParticle(Particle.END_ROD, d0, d1, d2, 0, f4, 0.01D, -f3, 1.0D); + } else { + double d3 = hook.getY() + 0.5D; + hook.getWorld().spawnParticle(Particle.END_ROD, hook.getX(), d3, hook.getZ(), (int) (1.0F + 0.3 * 20.0F), 0.3, 0.0D, 0.3, 0.20000000298023224D); + this.nibble = RandomUtils.generateRandomInt(20, 40); + this.hooked = true; + hook.getWorld().playSound(hook.getLocation(), Sound.ITEM_TRIDENT_THUNDER, 0.25F, 1.0F + (RandomUtils.generateRandomFloat(0,1)-RandomUtils.generateRandomFloat(0,1)) * 0.4F); + EventUtils.fireAndForget(new FishingHookStateEvent(context.getHolder(), hook, FishingHookStateEvent.State.BITE)); + } + } else if (timeUntilLured > 0) { + if (!freeze) { + timeUntilLured--; + } + f = 0.1F; + if (this.timeUntilLured < 20) { + f += (float) (20 - this.timeUntilLured) * 0.05F; + } else if (this.timeUntilLured < 40) { + f += (float) (40 - this.timeUntilLured) * 0.02F; + } else if (this.timeUntilLured < 60) { + f += (float) (60 - this.timeUntilLured) * 0.01F; + } + if (RandomUtils.generateRandomFloat(0, 1) < f) { + f1 = RandomUtils.generateRandomFloat(0.0F, 360.0F) * 0.017453292F; + f2 = RandomUtils.generateRandomFloat(25.0F, 60.0F); + d0 = hook.getX() + Math.sin(f1) * f2 * 0.1D; + d1 = hook.getY(); + d2 = hook.getZ() + Math.cos(f1) * f2 * 0.1D; + hook.getWorld().spawnParticle(Particle.DRAGON_BREATH, d0, d1, d2, 2 + RandomUtils.generateRandomInt(0,1), 0.10000000149011612D, 0.0D, 0.10000000149011612D, 0.0D); + } + if (this.timeUntilLured <= 0) { + this.fishAngle = RandomUtils.generateRandomFloat(0F, 360F); + this.timeUntilHooked = RandomUtils.generateRandomInt(20, 80); + EventUtils.fireAndForget(new FishingHookStateEvent(context.getHolder(), hook, FishingHookStateEvent.State.LURE)); + } + } else { + setWaitTime(finalEffect); + } + } + }, 1, 1, hook.getLocation()); + } + + @Override + public boolean isHooked() { + return hooked; + } + + @Override + public void destroy() { + if (this.tempEntity != null && this.tempEntity.isValid()) { + this.tempEntity.remove(); + } + if (this.task != null) { + this.task.cancel(); + } + freeze = false; + } + + @Override + public void freeze() { + freeze = true; + } + + @Override + public void unfreeze(Effect finalEffect) { + freeze = false; + } + + private void setWaitTime(Effect effect) { + int before = ThreadLocalRandom.current().nextInt(ConfigManager.voidMaxTime() - ConfigManager.voidMinTime() + 1) + ConfigManager.voidMinTime(); + int after = Math.max(ConfigManager.voidMinTime(), (int) (before * effect.waitTimeMultiplier() + effect.waitTimeAdder())); + BukkitCustomFishingPlugin.getInstance().debug("Wait time: " + before + " -> " + after + " ticks"); + this.timeUntilLured = after; + } + + private void setTempEntityProperties(ArmorStand entity) { + entity.setInvisible(true); + entity.setCollidable(false); + entity.setInvulnerable(true); + entity.setVisible(false); + entity.setCustomNameVisible(false); + entity.setSmall(true); + entity.setGravity(false); + entity.getPersistentDataContainer().set( + Objects.requireNonNull(NamespacedKey.fromString("temp-entity", BukkitCustomFishingPlugin.getInstance().getBoostrap())), + PersistentDataType.STRING, + "void" + ); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGame.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGame.java new file mode 100644 index 00000000..9a3e38e7 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGame.java @@ -0,0 +1,29 @@ +package net.momirealms.customfishing.api.mechanic.game; + +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.fishing.CustomFishingHook; + +import java.util.function.BiFunction; + +public abstract class AbstractGame implements Game { + + private final GameBasics basics; + private final String id; + + public AbstractGame(String id, GameBasics basics) { + this.basics = basics; + this.id = id; + } + + @Override + public String id() { + return id; + } + + @Override + public GamingPlayer start(CustomFishingHook hook, Effect effect) { + return gamingPlayerProvider().apply(hook, basics.toGameSetting(effect)); + } + + public abstract BiFunction gamingPlayerProvider(); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGamingPlayer.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGamingPlayer.java index 3a163850..de4b258d 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGamingPlayer.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGamingPlayer.java @@ -17,43 +17,46 @@ package net.momirealms.customfishing.api.mechanic.game; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.manager.FishingManager; -import net.momirealms.customfishing.api.mechanic.effect.Effect; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import org.bukkit.Material; -import org.bukkit.entity.FishHook; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.fishing.CustomFishingHook; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; import org.bukkit.entity.Player; -import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.ApiStatus; + +import java.util.concurrent.TimeUnit; public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable { - private final FishingManager manager; protected long deadline; protected boolean success; - protected CancellableTask task; - protected Player player; - protected GameSettings settings; - protected FishHook fishHook; + protected SchedulerTask task; + protected GameSetting settings; + protected CustomFishingHook hook; protected boolean isTimeOut; + private boolean valid = true; + private boolean firstFlag = true; - public AbstractGamingPlayer(Player player, FishHook hook, GameSettings settings) { - this.player = player; - this.fishHook = hook; + public AbstractGamingPlayer(CustomFishingHook hook, GameSetting settings) { + this.hook = hook; this.settings = settings; - this.manager = CustomFishingPlugin.get().getFishingManager(); - this.deadline = (long) (System.currentTimeMillis() + settings.getTime() * 1000L); + this.deadline = (long) (System.currentTimeMillis() + settings.time() * 1000L); this.arrangeTask(); } public void arrangeTask() { - this.task = CustomFishingPlugin.get().getScheduler().runTaskSyncTimer(this, fishHook.getLocation(), 1, 1); + this.task = BukkitCustomFishingPlugin.getInstance().getScheduler().asyncRepeating(this, 50, 50, TimeUnit.MILLISECONDS); + } + + @Override + public void destroy() { + if (task != null) task.cancel(); + valid = false; } @Override public void cancel() { - if (task != null && !task.isCancelled()) - task.cancel(); + destroy(); } @Override @@ -61,45 +64,53 @@ public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable { return success; } + @ApiStatus.Internal + public void internalRightClick() { + firstFlag = true; + handleRightClick(); + } + @Override - public boolean onRightClick() { + public void handleRightClick() { endGame(); - return true; + } + + @ApiStatus.Internal + public boolean internalLeftClick() { + if (firstFlag) { + firstFlag = false; + return false; + } + return handleLeftClick(); } @Override - public boolean onLeftClick() { + public boolean handleLeftClick() { return false; } @Override - public boolean onChat(String message) { + public boolean handleChat(String message) { return false; } @Override - public boolean onSwapHand() { + public void handleSwapHand() { + } + + @Override + public boolean handleJump() { return false; } @Override - public boolean onJump() { - return false; - } - - @Override - public boolean onSneak() { + public boolean handleSneak() { return false; } @Override public Player getPlayer() { - return player; - } - - @Override - public Effect getEffectReward() { - return null; + return hook.getContext().getHolder(); } @Override @@ -107,16 +118,27 @@ public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable { if (timeOutCheck()) { return; } - switchItemCheck(); - onTick(); + tick(); } - public void onTick() { - + @Override + public boolean isValid() { + return valid; } + protected abstract void tick(); + protected void endGame() { - this.manager.processGameResult(this); + destroy(); + boolean success = isSuccessful(); + BukkitCustomFishingPlugin.getInstance().getScheduler().sync().run(() -> { + if (success) { + hook.handleSuccessfulFishing(); + } else { + hook.handleFailedFishing(); + } + hook.end(); + }, hook.getHookEntity().getLocation()); } protected void setGameResult(boolean success) { @@ -124,22 +146,13 @@ public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable { } protected boolean timeOutCheck() { - if (System.currentTimeMillis() > deadline) { + long delta = deadline - System.currentTimeMillis(); + if (delta <= 0) { isTimeOut = true; - cancel(); endGame(); return true; } + hook.getContext().arg(ContextKeys.TIME_LEFT, String.format("%.1f", (double) delta / 1000)); return false; } - - protected void switchItemCheck() { - PlayerInventory playerInventory = player.getInventory(); - if (playerInventory.getItemInMainHand().getType() != Material.FISHING_ROD - && playerInventory.getItemInOffHand().getType() != Material.FISHING_ROD - ) { - cancel(); - endGame(); - } - } } 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 deleted file mode 100644 index 5983cc79..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/BasicGameConfig.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic.game; - -import net.momirealms.customfishing.api.mechanic.effect.Effect; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.ThreadLocalRandom; - -public class BasicGameConfig { - - private int minTime; - private int maxTime; - private int minDifficulty; - private int maxDifficulty; - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private final BasicGameConfig basicGameConfig; - - public Builder() { - 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; - return this; - } - - public Builder time(int value) { - basicGameConfig.minTime = (basicGameConfig.maxTime = value); - return this; - } - - public Builder time(int min, int max) { - basicGameConfig.minTime = min; - basicGameConfig.maxTime = max; - return this; - } - - public BasicGameConfig build() { - return 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( - ThreadLocalRandom.current().nextInt(minTime, maxTime + 1) * effect.getGameTimeMultiplier() + effect.getGameTime(), - (int) Math.min(100, Math.max(1, ThreadLocalRandom.current().nextInt(minDifficulty, maxDifficulty + 1) * effect.getDifficultyMultiplier() + effect.getDifficulty())) - ); - } -} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameInstance.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/Game.java similarity index 75% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameInstance.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/game/Game.java index cca32618..4e17e272 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameInstance.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/Game.java @@ -17,10 +17,12 @@ package net.momirealms.customfishing.api.mechanic.game; -import org.bukkit.entity.FishHook; -import org.bukkit.entity.Player; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.fishing.CustomFishingHook; -public interface GameInstance { +public interface Game { + + String id(); - GamingPlayer start(Player player, FishHook hook, GameSettings settings); + GamingPlayer start(CustomFishingHook hook, Effect effect); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameBasics.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameBasics.java new file mode 100644 index 00000000..d3f4cc09 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameBasics.java @@ -0,0 +1,35 @@ +package net.momirealms.customfishing.api.mechanic.game; + +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import org.jetbrains.annotations.NotNull; + +public interface GameBasics { + + int minTime(); + + int maxTime(); + + int minDifficulty(); + + int maxDifficulty(); + + static GameBasics.Builder builder() { + return new GameBasicsImpl.BuilderImpl(); + } + + @NotNull + GameSetting toGameSetting(Effect effect); + + interface Builder { + + Builder difficulty(int value); + + Builder difficulty(int min, int max); + + Builder time(int value); + + Builder time(int min, int max); + + GameBasics build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameBasicsImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameBasicsImpl.java new file mode 100644 index 00000000..20f51bc0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameBasicsImpl.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.game; + +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.common.util.RandomUtils; +import org.jetbrains.annotations.NotNull; + +public record GameBasicsImpl(int minTime, int maxTime, int minDifficulty, int maxDifficulty) implements GameBasics { + + public static class BuilderImpl implements Builder { + private int minTime; + private int maxTime; + private int minDifficulty; + private int maxDifficulty; + @Override + public Builder difficulty(int value) { + minDifficulty = (maxDifficulty = value); + return this; + } + @Override + public Builder difficulty(int min, int max) { + minDifficulty = min; + maxDifficulty = max; + return this; + } + @Override + public Builder time(int value) { + minTime = (maxTime = value); + return this; + } + @Override + public Builder time(int min, int max) { + minTime = min; + maxTime = max; + return this; + } + @Override + public GameBasics build() { + return new GameBasicsImpl(minTime, maxTime, minDifficulty, maxDifficulty); + } + } + + @Override + @NotNull + public GameSetting toGameSetting(Effect effect) { + return new GameSetting( + RandomUtils.generateRandomInt(minTime, maxTime) * effect.gameTimeMultiplier() + effect.gameTimeAdder(), + (int) Math.min(100, Math.max(1, RandomUtils.generateRandomInt(minDifficulty, maxDifficulty) * effect.difficultyMultiplier() + effect.difficultyAdder())) + ); + } +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameFactory.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameFactory.java index aad604a8..6e1c6892 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameFactory.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameFactory.java @@ -17,10 +17,9 @@ package net.momirealms.customfishing.api.mechanic.game; -import org.bukkit.configuration.ConfigurationSection; +import dev.dejvokep.boostedyaml.block.implementation.Section; public interface GameFactory { - GameInstance setArgs(ConfigurationSection section); - + Game create(String id, Section section); } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameManager.java similarity index 57% rename from api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameManager.java index 305a35af..38893364 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameManager.java @@ -15,18 +15,17 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.manager; +package net.momirealms.customfishing.api.mechanic.game; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.game.BasicGameConfig; -import net.momirealms.customfishing.api.mechanic.game.GameFactory; -import net.momirealms.customfishing.api.mechanic.game.GameInstance; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; +import java.util.Optional; -public interface GameManager { +public interface GameManager extends Reloadable { /** * Registers a new game type with the specified type identifier. @@ -51,22 +50,13 @@ public interface GameManager { * @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); + @Nullable + GameFactory getGameFactory(String 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. - */ - @Nullable Pair getGameInstance(String key); + Optional getGame(String id); - /** - * 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); + boolean registerGame(Game game); + + @Nullable + Game getNextGame(Effect effect, Context context); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/YamlPage.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameSetting.java similarity index 85% rename from plugin/src/main/java/net/momirealms/customfishing/gui/YamlPage.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameSetting.java index 53e7deba..f9b5468a 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/YamlPage.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameSetting.java @@ -15,9 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.gui; +package net.momirealms.customfishing.api.mechanic.game; -public interface YamlPage extends ParentPage { - - void save(); +public record GameSetting(double time, int difficulty) { } 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 4ca24a3c..8971b8fa 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 @@ -17,53 +17,29 @@ 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 - */ + boolean isValid(); + + void destroy(); + void cancel(); boolean isSuccessful(); - /** - * @return whether to cancel the event - */ - boolean onRightClick(); + void handleRightClick(); - /** - * @return whether to cancel the event - */ - boolean onSwapHand(); + void handleSwapHand(); - /** - * @return whether to cancel the event - */ - boolean onLeftClick(); + boolean handleLeftClick(); - /** - * @return whether to cancel the event - */ - boolean onChat(String message); + boolean handleChat(String message); - /** - * @return whether to cancel the event - */ - boolean onJump(); + boolean handleJump(); - /** - * @return whether to cancel the event - */ - boolean onSneak(); + boolean handleSneak(); Player getPlayer(); - - /** - * @return effect reward based on game results - */ - @Nullable Effect getEffectReward(); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/hook/HookConfig.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/hook/HookConfig.java new file mode 100644 index 00000000..3259c2f4 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/hook/HookConfig.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.hook; + +import java.util.List; + +public interface HookConfig { + + String id(); + + List lore(); + + static Builder builder() { + return new HookConfigImpl.BuilderImpl(); + } + + interface Builder { + + Builder id(String id); + + Builder lore(List lore); + + HookConfig build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/hook/HookSetting.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/hook/HookConfigImpl.java similarity index 52% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/hook/HookSetting.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/hook/HookConfigImpl.java index 9c4222f0..284b469d 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/hook/HookSetting.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/hook/HookConfigImpl.java @@ -17,51 +17,47 @@ package net.momirealms.customfishing.api.mechanic.hook; -import java.util.ArrayList; import java.util.List; -public class HookSetting { +public class HookConfigImpl implements HookConfig { - private final String key; - private int maxDurability; - private List lore; + private final String id; + private final int maxDurability; + private final List lore; - public HookSetting(String key) { - this.key = key; + public HookConfigImpl(String id, int maxDurability, List lore) { + this.id = id; + this.maxDurability = maxDurability; + this.lore = lore; } - public String getKey() { - return key; + @Override + public String id() { + return id; } - public int getMaxDurability() { - return maxDurability; + @Override + public List lore() { + return lore; } - public List getLore() { - return lore == null ? new ArrayList<>() : lore; - } - - public static class Builder { - - private final HookSetting setting; - - public Builder(String key) { - this.setting = new HookSetting(key); - } - - public Builder durability(int maxDurability) { - setting.maxDurability = maxDurability; + public static class BuilderImpl implements Builder { + private String id; + private int maxDurability; + private List lore; + @Override + public Builder id(String id) { + this.id = id; return this; } - + @Override public Builder lore(List lore) { - setting.lore = lore; + this.lore = lore; return this; } - - public HookSetting build() { - return setting; + @Override + public HookConfig build() { + return new HookConfigImpl(id, maxDurability, lore); } } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/hook/HookManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/hook/HookManager.java new file mode 100644 index 00000000..a58ca2f6 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/hook/HookManager.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.hook; + +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public interface HookManager extends Reloadable { + + boolean registerHook(HookConfig hook); + + @NotNull + Optional getHook(String id); + + Optional getHookID(ItemStack rod); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/CustomFishingItem.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/CustomFishingItem.java new file mode 100644 index 00000000..d9a3c997 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/CustomFishingItem.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.item; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.config.function.PriorityFunction; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.item.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.function.BiConsumer; + +public interface CustomFishingItem { + + String DEFAULT_MATERIAL = "PAPER"; + + /** + * Returns the material type of the custom fishing item. + * + * @return the material type as a String. + */ + String material(); + + String id(); + + /** + * Returns a list of tag consumers which are functions that take an item and context as parameters + * and perform some operation on them. + * + * @return a list of BiConsumer instances. + */ + List, Context>> tagConsumers(); + + default ItemStack build(Context context) { + return BukkitCustomFishingPlugin.getInstance().getItemManager().build(context, this); + } + + /** + * Creates a new Builder instance to construct a CustomFishingItem. + * + * @return a new Builder instance. + */ + static Builder builder() { + return new CustomFishingItemImpl.BuilderImpl(); + } + + /** + * Builder interface for constructing instances of CustomFishingItem. + */ + interface Builder { + + Builder id(String id); + + /** + * Sets the material type for the CustomFishingItem being built. + * + * @param material the material type as a String. + * @return the Builder instance for method chaining. + */ + Builder material(String material); + + /** + * Sets the list of tag consumers for the CustomFishingItem being built. + * + * @param tagConsumers a list of BiConsumer instances. + * @return the Builder instance for method chaining. + */ + Builder tagConsumers(List, Context>>> tagConsumers); + + /** + * Builds and returns a new CustomFishingItem instance. + * + * @return a new CustomFishingItem instance. + */ + CustomFishingItem build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/CustomFishingItemImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/CustomFishingItemImpl.java new file mode 100644 index 00000000..362a2af2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/CustomFishingItemImpl.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.item; + +import net.momirealms.customfishing.api.mechanic.config.function.PriorityFunction; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.item.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Optional; +import java.util.TreeSet; +import java.util.function.BiConsumer; + +import static java.util.Objects.requireNonNull; + +public class CustomFishingItemImpl implements CustomFishingItem { + + private final String material; + private final String id; + + private final List, Context>> tagConsumers; + + public CustomFishingItemImpl(String id, String material, List, Context>> tagConsumers) { + this.material = material; + this.id = id; + this.tagConsumers = tagConsumers; + } + + @Override + public String material() { + return Optional.ofNullable(material).orElse("AIR"); + } + + @Override + public String id() { + return id; + } + + @Override + public List, Context>> tagConsumers() { + return tagConsumers; + } + + public static class BuilderImpl implements Builder { + + private String material = DEFAULT_MATERIAL; + private String id; + private final TreeSet, Context>>> tagConsumers = new TreeSet<>(); + + @Override + public Builder id(String id) { + this.id = id; + return this; + } + + @Override + public Builder material(String material) { + this.material = material; + return this; + } + + @Override + public Builder tagConsumers(List, Context>>> tagConsumers) { + this.tagConsumers.addAll(tagConsumers); + return this; + } + + @Override + public CustomFishingItem build() { + return new CustomFishingItemImpl(requireNonNull(id), material, tagConsumers.stream().map(PriorityFunction::get).toList()); + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemBuilder.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemBuilder.java deleted file mode 100644 index 97d3e4fb..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemBuilder.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic.item; - -import de.tr7zw.changeme.nbtapi.NBTItem; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.common.Tuple; -import net.momirealms.customfishing.api.mechanic.misc.Value; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemFlag; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -public interface ItemBuilder extends BuildableItem { - - ItemBuilder customModelData(int value); - - ItemBuilder name(String name); - - ItemBuilder amount(int amount); - - ItemBuilder amount(int min_amount, int max_amount); - - ItemBuilder tag(boolean tag, String type, String id); - - ItemBuilder unbreakable(boolean unbreakable); - - ItemBuilder placeable(boolean placeable); - - ItemBuilder lore(List lore); - - ItemBuilder nbt(Map nbt); - - ItemBuilder itemFlag(List itemFlags); - - ItemBuilder nbt(ConfigurationSection section); - - ItemBuilder enchantment(List> enchantments, boolean store); - - ItemBuilder randomEnchantments(List> enchantments, boolean store); - - ItemBuilder enchantmentPool(List> amountPairs, List, Value>> enchantments, boolean store); - - ItemBuilder maxDurability(int max); - - ItemBuilder price(float base, float bonus); - - ItemBuilder size(Pair size); - - ItemBuilder stackable(boolean stackable); - - ItemBuilder preventGrabbing(boolean prevent); - - ItemBuilder head(String base64); - - ItemBuilder randomDamage(boolean damage); - - @NotNull - String getId(); - - @NotNull - String getLibrary(); - - int getAmount(); - - Collection getEditors(); - - ItemBuilder removeEditor(String type); - - ItemBuilder registerCustomEditor(String type, ItemPropertyEditor editor); - - interface ItemPropertyEditor { - - void edit(Player player, NBTItem nbtItem, Map placeholders); - - default void edit(Player player, NBTItem nbtItem) { - edit(player, nbtItem, null); - } - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemLibrary.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemEditor.java similarity index 79% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemLibrary.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemEditor.java index a4419a9b..902de141 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemLibrary.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemEditor.java @@ -17,14 +17,12 @@ package net.momirealms.customfishing.api.mechanic.item; +import com.saicone.rtag.RtagItem; +import net.momirealms.customfishing.api.mechanic.context.Context; import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -public interface ItemLibrary { +@FunctionalInterface +public interface ItemEditor { - String identification(); - - ItemStack buildItem(Player player, String id); - - String getItemID(ItemStack itemStack); + void apply(RtagItem item, Context context); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemManager.java new file mode 100644 index 00000000..cd1bac7c --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemManager.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.item; + +import com.saicone.rtag.RtagItem; +import net.momirealms.customfishing.api.integration.ItemProvider; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.item.ItemFactory; +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.bukkit.entity.FishHook; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +public interface ItemManager extends Reloadable { + + boolean registerItem(@NotNull CustomFishingItem item); + + @Nullable + ItemStack buildInternal(@NotNull Context context, @NotNull String id) throws NullPointerException; + + ItemStack build(@NotNull Context context, @NotNull CustomFishingItem item); + + @Nullable + ItemStack buildAny(@NotNull Context context, @NotNull String id); + + @NotNull + String getItemID(@NotNull ItemStack itemStack); + + @Nullable + String getCustomFishingItemID(@NotNull ItemStack itemStack); + + @Nullable + Item dropItemLoot(@NotNull Context context, ItemStack rod, FishHook hook); + + boolean hasCustomDurability(ItemStack itemStack); + + void decreaseDurability(Player player, ItemStack itemStack, int amount, boolean incorrectUsage); + + void setDurability(Player player, ItemStack itemStack, int damage); + + ItemFactory getFactory(); + + ItemProvider[] getItemProviders(); + + Collection getItemIDs(); + + net.momirealms.customfishing.common.item.Item wrap(ItemStack itemStack); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/tag/TagMap.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/tag/TagMap.java new file mode 100644 index 00000000..e0537e08 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/tag/TagMap.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.item.tag; + +import net.momirealms.customfishing.api.mechanic.context.Context; +import org.bukkit.entity.Player; + +import java.util.Map; + +public interface TagMap { + + Map apply(Context context); + + static TagMap of(Map inputMap) { + return new TagMapImpl(inputMap); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/tag/TagMapImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/tag/TagMapImpl.java new file mode 100644 index 00000000..5a608ece --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/tag/TagMapImpl.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.item.tag; + +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import net.momirealms.customfishing.api.mechanic.misc.value.TextValue; +import net.momirealms.customfishing.common.util.Pair; +import org.bukkit.entity.Player; + +import java.util.*; + +import static net.momirealms.customfishing.api.util.TagUtils.toTypeAndData; +import static net.momirealms.customfishing.common.util.ArrayUtils.splitValue; + +public class TagMapImpl implements TagMap { + + private final Map convertedMap = new HashMap<>(); + + public TagMapImpl(Map inputMap) { + this.analyze(inputMap, convertedMap); + } + + private void analyze(Map inputMap, Map outPutMap) { + for (Map.Entry entry : inputMap.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof Map inner) { + Map inputInnerMap = (Map) inner; + HashMap outputInnerMap = new HashMap<>(); + outPutMap.put(key, outputInnerMap); + analyze(inputInnerMap, outputInnerMap); + } else if (value instanceof List list) { + Object first = list.get(0); + ArrayList outputList = new ArrayList<>(); + if (first instanceof Map) { + for (Object o : list) { + Map inputListMap = (Map) o; + outputList.add(TagMap.of(inputListMap)); + } + } else if (first instanceof String) { + for (Object o : list) { + String str = (String) o; + Pair pair = toTypeAndData(str); + switch (pair.left()) { + case STRING -> { + TextValue textValue = TextValue.auto(pair.right()); + outputList.add((ValueProvider) textValue::render); + } + case BYTE -> { + MathValue mathValue = MathValue.auto(pair.right()); + outputList.add((ValueProvider) context -> (byte) mathValue.evaluate(context)); + } + case SHORT -> { + MathValue mathValue = MathValue.auto(pair.right()); + outputList.add((ValueProvider) context -> (short) mathValue.evaluate(context)); + } + case INT -> { + MathValue mathValue = MathValue.auto(pair.right()); + outputList.add((ValueProvider) context -> (int) mathValue.evaluate(context)); + } + case LONG -> { + MathValue mathValue = MathValue.auto(pair.right()); + outputList.add((ValueProvider) context -> (long) mathValue.evaluate(context)); + } + case FLOAT -> { + MathValue mathValue = MathValue.auto(pair.right()); + outputList.add((ValueProvider) context -> (float) mathValue.evaluate(context)); + } + case DOUBLE -> { + MathValue mathValue = MathValue.auto(pair.right()); + outputList.add((ValueProvider) context -> (double) mathValue.evaluate(context)); + } + } + } + } else { + outputList.addAll(list); + } + outPutMap.put(key, outputList); + } else if (value instanceof String str) { + Pair pair = toTypeAndData(str); + switch (pair.left()) { + case INTARRAY -> { + String[] split = splitValue(str); + int[] array = Arrays.stream(split).mapToInt(Integer::parseInt).toArray(); + outPutMap.put(pair.right(), array); + } + case BYTEARRAY -> { + String[] split = splitValue(str); + byte[] bytes = new byte[split.length]; + for (int i = 0; i < split.length; i++){ + bytes[i] = Byte.parseByte(split[i]); + } + outPutMap.put(pair.right(), bytes); + } + case STRING -> { + TextValue textValue = TextValue.auto(pair.right()); + outPutMap.put(key, (ValueProvider) textValue::render); + } + case BYTE -> { + MathValue mathValue = MathValue.auto(pair.right()); + outPutMap.put(key, (ValueProvider) context -> (byte) mathValue.evaluate(context)); + } + case SHORT -> { + MathValue mathValue = MathValue.auto(pair.right()); + outPutMap.put(key, (ValueProvider) context -> (short) mathValue.evaluate(context)); + } + case INT -> { + MathValue mathValue = MathValue.auto(pair.right()); + outPutMap.put(key, (ValueProvider) context -> (int) mathValue.evaluate(context)); + } + case LONG -> { + MathValue mathValue = MathValue.auto(pair.right()); + outPutMap.put(key, (ValueProvider) context -> (long) mathValue.evaluate(context)); + } + case FLOAT -> { + MathValue mathValue = MathValue.auto(pair.right()); + outPutMap.put(key, (ValueProvider) context -> (float) mathValue.evaluate(context)); + } + case DOUBLE -> { + MathValue mathValue = MathValue.auto(pair.right()); + outPutMap.put(key, (ValueProvider) context -> (double) mathValue.evaluate(context)); + } + } + } else { + outPutMap.put(key, value); + } + } + } + + @Override + public Map apply(Context context) { + HashMap output = new HashMap<>(); + setMapValue(convertedMap, output, context); + return output; + } + + private void setMapValue(Map inputMap, Map outPutMap, Context context) { + for (Map.Entry entry : inputMap.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof Map inner) { + Map inputInnerMap = (Map) inner; + HashMap outputInnerMap = new HashMap<>(); + outPutMap.put(key, outputInnerMap); + setMapValue(inputInnerMap, outputInnerMap, context); + } else if (value instanceof List list) { + ArrayList convertedList = new ArrayList<>(); + Object first = list.get(0); + if (first instanceof TagMap) { + for (Object o : list) { + TagMap map = (TagMap) o; + convertedList.add(map.apply(context)); + } + } else if (first instanceof ValueProvider) { + for (Object o : list) { + ValueProvider pd = (ValueProvider) o; + convertedList.add(pd.apply(context)); + } + } else { + convertedList.addAll(list); + } + outPutMap.put(key, convertedList); + } else if (value instanceof ValueProvider provider) { + outPutMap.put(key, provider.apply(context)); + } else { + outPutMap.put(key, value); + } + } + } + + @FunctionalInterface + public interface ValueProvider { + Object apply(Context context); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/tag/TagValueType.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/tag/TagValueType.java new file mode 100644 index 00000000..bacfa32d --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/tag/TagValueType.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.item.tag; + +public enum TagValueType { + BYTE, + INT, + DOUBLE, + LONG, + FLOAT, + SHORT, + STRING, + BYTEARRAY, + INTARRAY +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/CFLoot.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/CFLoot.java deleted file mode 100644 index 0b7419a3..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/CFLoot.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic.loot; - -import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.effect.BaseEffect; -import net.momirealms.customfishing.api.mechanic.statistic.StatisticsKey; -import org.jetbrains.annotations.NotNull; - -import java.util.HashMap; - -public class CFLoot implements Loot { - - private final String id; - private final LootType type; - private final HashMap actionMap; - private final HashMap successTimesActionMap; - private String nick; - private boolean showInFinder; - private boolean disableGame; - private boolean disableGlobalAction; - private boolean disableStats; - private boolean instanceGame; - private double score; - private String[] lootGroup; - private StatisticsKey statisticsKey; - private BaseEffect effect; - - public CFLoot(String id, LootType type) { - this.id = id; - this.type = type; - this.actionMap = new HashMap<>(); - this.successTimesActionMap = new HashMap<>(); - } - - public static Builder builder(String id, LootType type) { - return new Builder(id, type); - } - - /** - * Builder class for CFLoot. - */ - public static class Builder { - - private final CFLoot loot; - - public Builder(String id, LootType type) { - this.loot = new CFLoot(id, type); - } - - /** - * Set the file path for this loot. - * - * @param path file path - * @return The builder. - */ - public Builder filePath(String path) { - return this; - } - - /** - * Set the nickname for this loot. - * - * @param nick The nickname. - * @return The builder. - */ - public Builder nick(String nick) { - this.loot.nick = nick; - return this; - } - - /** - * Set whether this loot should be shown in the finder. - * - * @param show True if it should be shown, false otherwise. - * @return The builder. - */ - public Builder showInFinder(boolean show) { - this.loot.showInFinder = show; - return this; - } - - /** - * Set whether this loot should have an instance game. - * - * @param instant True if it should be an instance game, false otherwise. - * @return The builder. - */ - public Builder instantGame(boolean instant) { - this.loot.instanceGame = instant; - return this; - } - - /** - * Set whether games are disabled for this loot. - * - * @param disable True if games are disabled, false otherwise. - * @return The builder. - */ - public Builder disableGames(boolean disable) { - this.loot.disableGame = disable; - return this; - } - - /** - * Set whether statistics are disabled for this loot. - * - * @param disable True if statistics are disabled, false otherwise. - * @return The builder. - */ - public Builder disableStats(boolean disable) { - this.loot.disableStats = disable; - return this; - } - - /** - * Set whether global actions are disabled for this loot. - * - * @param disable True if statistics are disabled, false otherwise. - * @return The builder. - */ - public Builder disableGlobalActions(boolean disable) { - this.loot.disableGlobalAction = disable; - return this; - } - - /** - * Set the score for this loot. - * - * @param score The score. - * @return The builder. - */ - public Builder score(double score) { - this.loot.score = score; - return this; - } - - /** - * Set the loot group for this loot. - * - * @param groups The loot group. - * @return The builder. - */ - public Builder lootGroup(String[] groups) { - this.loot.lootGroup = groups; - return this; - } - - /** - * Set the statistics key for this loot - * - * @param statisticsKey statistics key - * @return The builder. - */ - public Builder statsKey(StatisticsKey statisticsKey) { - this.loot.statisticsKey = statisticsKey; - return this; - } - - /** - * Set the effects for the loot - * - * @param effect effect - * @return The builder. - */ - public Builder baseEffect(BaseEffect effect) { - this.loot.effect = effect; - return this; - } - - /** - * Add actions triggered by a specific trigger. - * - * @param trigger The trigger for the actions. - * @param actions The actions to add. - * @return The builder. - */ - public Builder addActions(ActionTrigger trigger, Action[] actions) { - this.loot.actionMap.put(trigger, actions); - return this; - } - - /** - * Add actions triggered by multiple triggers. - * - * @param actionMap A map of triggers to actions. - * @return The builder. - */ - public Builder addActions(HashMap actionMap) { - this.loot.actionMap.putAll(actionMap); - return this; - } - - /** - * Add actions triggered by the number of successes. - * - * @param times The number of successes for triggering the actions. - * @param actions The actions to add. - * @return The builder. - */ - public Builder addTimesActions(int times, Action[] actions) { - this.loot.successTimesActionMap.put(times, actions); - return this; - } - - /** - * Add actions triggered by multiple numbers of successes. - * - * @param actionMap A map of numbers of successes to actions. - * @return The builder. - */ - public Builder addTimesActions(HashMap actionMap) { - this.loot.successTimesActionMap.putAll(actionMap); - return this; - } - - /** - * Build the CFLoot object. - * - * @return The built CFLoot object. - */ - public CFLoot build() { - return loot; - } - } - - @Override - public boolean instanceGame() { - return this.instanceGame; - } - - @Override - public String getID() { - return this.id; - } - - @Override - public LootType getType() { - return this.type; - } - - @Override - public @NotNull String getNick() { - return this.nick; - } - - @Override - public StatisticsKey getStatisticKey() { - return this.statisticsKey; - } - - @Override - public boolean showInFinder() { - return this.showInFinder; - } - - @Override - public double getScore() { - return this.score; - } - - @Override - public boolean disableGame() { - return this.disableGame; - } - - @Override - public boolean disableStats() { - return this.disableStats; - } - - @Override - public boolean disableGlobalAction() { - return this.disableGlobalAction; - } - - @Override - public String[] getLootGroup() { - return lootGroup; - } - - @Override - public Action[] getActions(ActionTrigger actionTrigger) { - return actionMap.get(actionTrigger); - } - - @Override - public void triggerActions(ActionTrigger actionTrigger, Condition condition) { - Action[] actions = getActions(actionTrigger); - if (actions != null) { - for (Action action : actions) { - action.trigger(condition); - } - } - } - - @Override - public BaseEffect getBaseEffect() { - return effect; - } - - @Override - public Action[] getSuccessTimesActions(int times) { - return successTimesActionMap.get(times); - } - - @Override - public HashMap getSuccessTimesActionMap() { - return successTimesActionMap; - } -} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/Loot.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/Loot.java index 020c4991..c8dc965b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/Loot.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/Loot.java @@ -17,67 +17,30 @@ package net.momirealms.customfishing.api.mechanic.loot; -import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.effect.BaseEffect; -import net.momirealms.customfishing.api.mechanic.statistic.StatisticsKey; +import net.momirealms.customfishing.api.mechanic.effect.LootBaseEffect; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import net.momirealms.customfishing.api.mechanic.statistic.StatisticsKeys; +import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; public interface Loot { + class DefaultProperties { + public static boolean DEFAULT_INSTANT_GAME = false; + public static boolean DEFAULT_DISABLE_GAME = false; + public static boolean DEFAULT_DISABLE_STATS = false; + public static boolean DEFAULT_SHOW_IN_FINDER = true; + } + + LootType DEFAULT_TYPE = LootType.ITEM; + MathValue DEFAULT_SCORE = MathValue.plain(0); + /** - * Check if this loot has an instance game. + * Check if this loot triggers an instant game. * - * @return True if it's an instance game, false otherwise. + * @return True if it triggers an instant game, false otherwise. */ - boolean instanceGame(); - - /** - * Check if the loot disables global actions - */ - boolean disableGlobalAction(); - - /** - * Get the unique ID of this loot. - * - * @return The unique ID. - */ - String getID(); - - /** - * Get the type of this loot. - * - * @return The loot type. - */ - LootType getType(); - - /** - * Get the nickname of this loot. - * - * @return The nickname. - */ - @NotNull - String getNick(); - - StatisticsKey getStatisticKey(); - - /** - * Check if this loot should be shown in the finder. - * - * @return True if it should be shown, false otherwise. - */ - boolean showInFinder(); - - /** - * Get the score of this loot. - * - * @return The score. - */ - double getScore(); + boolean instantGame(); /** * Check if games are disabled for this loot. @@ -87,55 +50,180 @@ public interface Loot { boolean disableGame(); /** - * Check if statistics are disabled for this loot. + * Check if statistics recording is disabled for this loot. * * @return True if statistics are disabled, false otherwise. */ boolean disableStats(); /** - * Get the loot group of this loot. + * Check if this loot should be displayed in the finder tool. * - * @return The loot group. + * @return True if it should be shown in the finder, false otherwise. */ - String[] getLootGroup(); + boolean showInFinder(); + + boolean preventGrabbing(); /** - * Get the actions triggered by a specific action trigger. + * Get the unique identifier for this loot. * - * @param actionTrigger The action trigger. - * @return The actions triggered by the given trigger. + * @return The unique ID of the loot. */ - @Nullable - Action[] getActions(ActionTrigger actionTrigger); + String id(); /** - * Trigger actions associated with a specific action trigger. + * Get the type of this loot. * - * @param actionTrigger The action trigger. - * @param condition The condition under which the actions are triggered. + * @return The type of the loot. */ - void triggerActions(ActionTrigger actionTrigger, Condition condition); + LootType type(); /** - * Get effects that bond to this loot + * Get the display nickname for this loot. * - * @return effects + * @return The nickname of the loot. */ - BaseEffect getBaseEffect(); + @NotNull + String nick(); /** - * Get the actions triggered by a specific number of successes. + * Get the statistics key associated with this loot. * - * @param times The number of successes. - * @return The actions triggered by the specified number of successes. + * @return The statistics key for this loot. */ - Action[] getSuccessTimesActions(int times); + StatisticsKeys statisticKey(); /** - * Get a map of actions triggered by different numbers of successes. + * Get the score value for this loot. * - * @return A map of actions triggered by success times. + * @return The score associated with the loot. */ - HashMap getSuccessTimesActionMap(); + MathValue score(); + + /** + * Get the groups this loot belongs to. + * + * @return An array of group names. + */ + String[] lootGroup(); + + /** + * Get the base effect associated with this loot. + * + * @return The base effect for the loot. + */ + LootBaseEffect baseEffect(); + + /** + * Create a new builder for constructing a Loot instance. + * + * @return A new Loot builder. + */ + static Builder builder() { + return new LootImpl.BuilderImpl(); + } + + /** + * Builder interface for constructing instances of Loot. + */ + interface Builder { + + /** + * Set the type of the loot. + * + * @param type The type of the loot. + * @return The builder instance. + */ + Builder type(LootType type); + + /** + * Specify whether the loot triggers an instant game. + * + * @param instantGame True if it should trigger an instant game. + * @return The builder instance. + */ + Builder instantGame(boolean instantGame); + + /** + * Specify whether games are disabled for this loot. + * + * @param disableGame True if games should be disabled. + * @return The builder instance. + */ + Builder disableGame(boolean disableGame); + + Builder preventGrabbing(boolean preventGrabbing); + + /** + * Specify whether statistics recording is disabled for this loot. + * + * @param disableStatistics True if statistics should be disabled. + * @return The builder instance. + */ + Builder disableStatistics(boolean disableStatistics); + + /** + * Specify whether the loot should be shown in the finder tool. + * + * @param showInFinder True if it should be shown in the finder. + * @return The builder instance. + */ + Builder showInFinder(boolean showInFinder); + + /** + * Set the unique ID for the loot. + * + * @param id The unique identifier. + * @return The builder instance. + */ + Builder id(String id); + + /** + * Set the nickname for the loot. + * + * @param nick The nickname. + * @return The builder instance. + */ + Builder nick(String nick); + + /** + * Set the statistics key for the loot. + * + * @param statisticsKeys The statistics key. + * @return The builder instance. + */ + Builder statisticsKeys(StatisticsKeys statisticsKeys); + + /** + * Set the score for the loot. + * + * @param score The score value. + * @return The builder instance. + */ + Builder score(MathValue score); + + /** + * Set the groups that the loot belongs to. + * + * @param groups An array of group names. + * @return The builder instance. + */ + Builder groups(String[] groups); + + /** + * Set the base effect for the loot. + * + * @param lootBaseEffect The base effect. + * @return The builder instance. + */ + Builder lootBaseEffect(LootBaseEffect lootBaseEffect); + + /** + * Build and return the Loot instance. + * + * @return The constructed Loot instance. + */ + Loot build(); + } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootImpl.java new file mode 100644 index 00000000..093d75f2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootImpl.java @@ -0,0 +1,214 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.loot; + +import net.momirealms.customfishing.api.mechanic.effect.LootBaseEffect; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import net.momirealms.customfishing.api.mechanic.statistic.StatisticsKeys; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class LootImpl implements Loot { + + private final LootType type; + private final boolean instantGame; + private final boolean disableGame; + private final boolean disableStatistics; + private final boolean showInFinder; + private final boolean preventGrabbing; + private final String id; + private final String nick; + private final StatisticsKeys statisticsKeys; + private final MathValue score; + private final String[] groups; + private final LootBaseEffect lootBaseEffect; + + public LootImpl(LootType type, boolean instantGame, boolean disableGame, boolean disableStatistics, boolean showInFinder, boolean preventGrabbing, String id, String nick, StatisticsKeys statisticsKeys, MathValue score, String[] groups, LootBaseEffect lootBaseEffect) { + this.type = type; + this.instantGame = instantGame; + this.disableGame = disableGame; + this.disableStatistics = disableStatistics; + this.showInFinder = showInFinder; + this.id = id; + this.nick = nick; + this.statisticsKeys = statisticsKeys; + this.score = score; + this.groups = groups; + this.lootBaseEffect = lootBaseEffect; + this.preventGrabbing = preventGrabbing; + } + + @Override + public boolean instantGame() { + return instantGame; + } + + @Override + public String id() { + return id; + } + + @Override + public LootType type() { + return type; + } + + @NotNull + @Override + public String nick() { + return nick; + } + + @Override + public StatisticsKeys statisticKey() { + return statisticsKeys; + } + + @Override + public boolean showInFinder() { + return showInFinder; + } + + @Override + public boolean preventGrabbing() { + return preventGrabbing; + } + + @Override + public MathValue score() { + return score; + } + + @Override + public boolean disableGame() { + return disableGame; + } + + @Override + public boolean disableStats() { + return disableStatistics; + } + + @Override + public String[] lootGroup() { + return groups; + } + + @Override + public LootBaseEffect baseEffect() { + return lootBaseEffect; + } + + public static class BuilderImpl implements Builder { + + private LootType type = DEFAULT_TYPE; + private boolean instantGame = Loot.DefaultProperties.DEFAULT_INSTANT_GAME; + private boolean disableGame = Loot.DefaultProperties.DEFAULT_DISABLE_GAME; + private boolean disableStatistics = Loot.DefaultProperties.DEFAULT_DISABLE_STATS; + private boolean showInFinder = Loot.DefaultProperties.DEFAULT_SHOW_IN_FINDER; + private boolean preventGrabbing = false; + private String id = null; + private String nick = "UNDEFINED"; + private StatisticsKeys statisticsKeys = null; + private MathValue score = DEFAULT_SCORE; + private String[] groups = new String[0]; + private LootBaseEffect lootBaseEffect = null; + + @Override + public Builder type(LootType type) { + this.type = type; + return this; + } + @Override + public Builder instantGame(boolean instantGame) { + this.instantGame = instantGame; + return this; + } + @Override + public Builder disableGame(boolean disableGame) { + this.disableGame = disableGame; + return this; + } + @Override + public Builder preventGrabbing(boolean preventGrabbing) { + this.preventGrabbing = preventGrabbing; + return this; + } + @Override + public Builder disableStatistics(boolean disableStatistics) { + this.disableStatistics = disableStatistics; + return this; + } + @Override + public Builder showInFinder(boolean showInFinder) { + this.showInFinder = showInFinder; + return this; + } + @Override + public Builder id(String id) { + this.id = id; + return this; + } + @Override + public Builder nick(String nick) { + this.nick = nick; + return this; + } + @Override + public Builder statisticsKeys(StatisticsKeys statisticsKeys) { + this.statisticsKeys = statisticsKeys; + return this; + } + @Override + public Builder score(MathValue score) { + this.score = score; + return this; + } + @Override + public Builder groups(String[] groups) { + this.groups = groups; + return this; + } + @Override + public Builder lootBaseEffect(LootBaseEffect lootBaseEffect) { + this.lootBaseEffect = lootBaseEffect; + return this; + } + @Override + public Loot build() { + return new LootImpl( + type, + instantGame, + disableGame, + disableStatistics, + showInFinder, + preventGrabbing, + requireNonNull(id), + Optional.ofNullable(nick).orElse(id), + Optional.ofNullable(statisticsKeys).orElse(new StatisticsKeys(id, id)), + score, + groups, + requireNonNull(lootBaseEffect) + ); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackGroundItem.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootManager.java similarity index 50% rename from plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackGroundItem.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootManager.java index 057c68a5..02316584 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackGroundItem.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootManager.java @@ -15,27 +15,31 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.gui.icon; +package net.momirealms.customfishing.api.mechanic.loot; -import net.momirealms.customfishing.gui.Icon; -import org.bukkit.Material; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; +import org.jetbrains.annotations.Nullable; -public class BackGroundItem extends AbstractItem implements Icon { +import java.util.List; +import java.util.Map; +import java.util.Optional; - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.BLACK_STAINED_GLASS_PANE).setDisplayName(""); - } +public interface LootManager extends Reloadable { - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + boolean registerLoot(@NotNull Loot loot); - } -} \ No newline at end of file + @NotNull + List getGroupMembers(String key); + + @NotNull + Optional getLoot(String key); + + Map getWeightedLoots(Effect effect, Context context); + + @Nullable + Loot getNextLoot(Effect effect, Context context); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootType.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootType.java index 784e6c49..9d0a967b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootType.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootType.java @@ -21,6 +21,10 @@ public enum LootType { ITEM, ENTITY, - BLOCK, - GLOBAL + BLOCK; + + @Override + public String toString() { + return name(); + } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/BuildableItem.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/market/MarketManager.java similarity index 60% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/item/BuildableItem.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/market/MarketManager.java index 9ac17591..2e37b69e 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/BuildableItem.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/market/MarketManager.java @@ -15,28 +15,20 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.mechanic.item; +package net.momirealms.customfishing.api.mechanic.market; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import java.util.HashMap; -import java.util.Map; +public interface MarketManager extends Reloadable { -public interface BuildableItem { + boolean openMarketGUI(Player player); - default ItemStack build() { - return build(null, new HashMap<>()); - } + double getItemPrice(Context context, ItemStack itemStack); - default ItemStack build(Player player) { - return build(player, new HashMap<>()); - } + String getFormula(); - ItemStack build(Player player, Map placeholders); - - /** - * Whether the item would be removed from cache when reloading - */ - boolean persist(); + double earningLimit(Context context); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/WeightModifier.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/WeightModifier.java deleted file mode 100644 index d481639a..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/WeightModifier.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic.misc; - -import org.bukkit.entity.Player; - -public interface WeightModifier { - - double modify(Player player, double weight); -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/CoolDownManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/cooldown/CoolDownManager.java similarity index 80% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/CoolDownManager.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/cooldown/CoolDownManager.java index acfffad8..024ab4e7 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/CoolDownManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/cooldown/CoolDownManager.java @@ -15,16 +15,19 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.misc; +package net.momirealms.customfishing.api.mechanic.misc.cooldown; -import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; +import java.util.Collections; import java.util.HashMap; +import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -32,12 +35,12 @@ 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 { +public class CoolDownManager implements Listener, Reloadable { private final ConcurrentHashMap dataMap; - private final CustomFishingPlugin plugin; + private final BukkitCustomFishingPlugin plugin; - public CoolDownManager(CustomFishingPlugin plugin) { + public CoolDownManager(BukkitCustomFishingPlugin plugin) { this.dataMap = new ConcurrentHashMap<>(); this.plugin = plugin; } @@ -55,14 +58,17 @@ public class CoolDownManager implements Listener { return data.isCoolDown(key, time); } + @Override public void load() { - Bukkit.getPluginManager().registerEvents(this, plugin); + Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap()); } + @Override public void unload() { HandlerList.unregisterAll(this); } + @Override public void disable() { unload(); this.dataMap.clear(); @@ -80,10 +86,10 @@ public class CoolDownManager implements Listener { public static class Data { - private final HashMap coolDownMap; + private final Map coolDownMap; public Data() { - this.coolDownMap = new HashMap<>(); + this.coolDownMap = Collections.synchronizedMap(new HashMap<>()); } /** @@ -93,7 +99,7 @@ public class CoolDownManager implements Listener { * @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) { + public boolean isCoolDown(String key, long delay) { long time = System.currentTimeMillis(); long last = coolDownMap.getOrDefault(key, time - delay); if (last + delay > time) { diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/placeholder/BukkitPlaceholderManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/placeholder/BukkitPlaceholderManager.java new file mode 100644 index 00000000..66043318 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/placeholder/BukkitPlaceholderManager.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.misc.placeholder; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.stream.Collectors; + +public class BukkitPlaceholderManager implements PlaceholderManager { + + private final BukkitCustomFishingPlugin plugin; + private boolean hasPapi; + private final HashMap customPlaceholderMap; + private static BukkitPlaceholderManager instance; + + public BukkitPlaceholderManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + this.customPlaceholderMap = new HashMap<>(); + instance = this; + } + + @Override + public void reload() { + this.hasPapi = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"); + } + + public static BukkitPlaceholderManager getInstance() { + return instance; + } + + @Override + public boolean registerCustomPlaceholder(String placeholder, String original) { + if (this.customPlaceholderMap.containsKey(placeholder)) return false; + this.customPlaceholderMap.put(placeholder, original); + return true; + } + + @Override + public List resolvePlaceholders(String text) { + List placeholders = new ArrayList<>(); + Matcher matcher = PATTERN.matcher(text); + while (matcher.find()) placeholders.add(matcher.group()); + return placeholders; + } + + private String setPlaceholders(OfflinePlayer player, String text) { + return hasPapi ? PlaceholderAPIUtils.parse(player, text) : text; + } + + @Override + public String parseSingle(@Nullable OfflinePlayer player, String placeholder, Map replacements) { + String result = null; + if (replacements != null) + result = replacements.get(placeholder); + if (result != null) + return result; + String custom = customPlaceholderMap.get(placeholder); + if (custom == null) + return placeholder; + return setPlaceholders(player, custom); + } + + @Override + public String parse(@Nullable OfflinePlayer player, String text, Map replacements) { + var list = resolvePlaceholders(text); + for (String papi : list) { + String replacer = null; + if (replacements != null) { + replacer = replacements.get(papi); + } + if (replacer == null) { + String custom = customPlaceholderMap.get(papi); + if (custom != null) + replacer = setPlaceholders(player, parse(player, custom, replacements)); + } + if (replacer != null) + text = text.replace(papi, replacer); + } + 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()); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/placeholder/PlaceholderAPIUtils.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/placeholder/PlaceholderAPIUtils.java new file mode 100644 index 00000000..b23f1a3f --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/placeholder/PlaceholderAPIUtils.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.misc.placeholder; + +import me.clip.placeholderapi.PlaceholderAPI; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +/** + * Utility class for interacting with the PlaceholderAPI. + * Provides methods to parse placeholders in strings for both online and offline players. + */ +public class PlaceholderAPIUtils { + + /** + * Parses placeholders in the provided text for an online player. + * + * @param player The online player for whom the placeholders should be parsed. + * @param text The text containing placeholders to be parsed. + * @return The text with parsed placeholders. + */ + public static String parse(Player player, String text) { + return PlaceholderAPI.setPlaceholders(player, text); + } + + /** + * Parses placeholders in the provided text for an offline player. + * + * @param player The offline player for whom the placeholders should be parsed. + * @param text The text containing placeholders to be parsed. + * @return The text with parsed placeholders. + */ + public static String parse(OfflinePlayer player, String text) { + return PlaceholderAPI.setPlaceholders(player, text); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/placeholder/PlaceholderManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/placeholder/PlaceholderManager.java new file mode 100644 index 00000000..94cda0bd --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/placeholder/PlaceholderManager.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.misc.placeholder; + +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +public interface PlaceholderManager extends Reloadable { + + Pattern PATTERN = Pattern.compile("\\{[^{}]+}"); + + /** + * Registers a custom placeholder with its corresponding original string. + * + * @param placeholder the placeholder to register. + * @param original the original string corresponding to the placeholder. + * @return true if the placeholder was successfully registered, false if it already exists. + */ + boolean registerCustomPlaceholder(String placeholder, String original); + + /** + * Resolves all placeholders within a given text. + * + * @param text the text to resolve placeholders in. + * @return a list of found placeholders. + */ + List resolvePlaceholders(String text); + + /** + * Parses a single placeholder for a specified player, optionally using a map of replacements. + * + * @param player the player for whom the placeholder should be parsed. + * @param placeholder the placeholder to parse. + * @param replacements a map of replacement strings for placeholders. + * @return the parsed placeholder string. + */ + String parseSingle(@Nullable OfflinePlayer player, String placeholder, Map replacements); + + /** + * Parses all placeholders in the given text for a specified player, optionally using a map of replacements. + * + * @param player the player for whom the placeholders should be parsed. + * @param text the text containing placeholders. + * @param replacements a map of replacement strings for placeholders. + * @return the text with parsed placeholders. + */ + String parse(@Nullable OfflinePlayer player, String text, Map replacements); + + /** + * Parses all placeholders in a list of strings for a specified player, optionally using a map of replacements. + * + * @param player the player for whom the placeholders should be parsed. + * @param list the list of strings containing placeholders. + * @param replacements a map of replacement strings for placeholders. + * @return the list of strings with parsed placeholders. + */ + List parse(@Nullable OfflinePlayer player, List list, Map replacements); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/season/Season.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/season/Season.java new file mode 100644 index 00000000..02c897ba --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/season/Season.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.misc.season; + +public enum Season { + SPRING, + SUMMER, + AUTUMN, + WINTER, + DISABLE +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/DynamicText.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/DynamicText.java similarity index 75% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/DynamicText.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/DynamicText.java index 4e72eac2..356225eb 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/DynamicText.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/DynamicText.java @@ -15,9 +15,9 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.misc; +package net.momirealms.customfishing.api.mechanic.misc.value; -import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; +import net.momirealms.customfishing.api.mechanic.misc.placeholder.BukkitPlaceholderManager; import org.bukkit.entity.Player; import java.util.ArrayList; @@ -39,7 +39,7 @@ 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)); + List placeholdersOwner = new ArrayList<>(BukkitPlaceholderManager.getInstance().resolvePlaceholders(value)); String origin = value; for (String placeholder : placeholdersOwner) { origin = origin.replace(placeholder, "%s"); @@ -54,22 +54,20 @@ 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(); + BukkitPlaceholderManager bukkitPlaceholderManager = BukkitPlaceholderManager.getInstance(); if ("%s".equals(originalValue)) { - string = placeholderManagerImpl.getSingleValue(owner, this.placeholders[0], placeholders); + string = bukkitPlaceholderManager.parseSingle(owner, this.placeholders[0], placeholders); } else { Object[] values = new String[this.placeholders.length]; for (int i = 0; i < this.placeholders.length; i++) { - values[i] = placeholderManagerImpl.getSingleValue(owner, this.placeholders[i], placeholders); + values[i] = bukkitPlaceholderManager.parseSingle(owner, this.placeholders[i], placeholders); } string = String.format(originalValue, values); } } if (!latestValue.equals(string)) { - // If the updated value is different from the latest value, update it. latestValue = string; return true; } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/ExpressionMathValueImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/ExpressionMathValueImpl.java new file mode 100644 index 00000000..99d77e08 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/ExpressionMathValueImpl.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.misc.value; + +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.misc.placeholder.BukkitPlaceholderManager; +import net.momirealms.customfishing.common.helper.ExpressionHelper; +import org.bukkit.OfflinePlayer; + +import java.util.Map; + +public class ExpressionMathValueImpl implements MathValue { + + private final String raw; + + public ExpressionMathValueImpl(String raw) { + this.raw = raw; + } + + @Override + public double evaluate(Context context) { + Map replacements = context.placeholderMap(); + String expression; + if (context.getHolder() instanceof OfflinePlayer player) expression = BukkitPlaceholderManager.getInstance().parse(player, raw, replacements); + else expression = BukkitPlaceholderManager.getInstance().parse(null, raw, replacements); + return ExpressionHelper.evaluate(expression); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/MathValue.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/MathValue.java new file mode 100644 index 00000000..4e81c549 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/MathValue.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.misc.value; + +import net.momirealms.customfishing.api.mechanic.context.Context; + +/** + * The MathValue interface represents a mathematical value that can be evaluated + * within a specific context. This interface allows for the evaluation of mathematical + * expressions or plain numerical values in the context of custom fishing mechanics. + * + * @param the type of the holder object for the context + */ +public interface MathValue { + + /** + * Evaluates the mathematical value within the given context. + * + * @param context the context in which the value is evaluated + * @return the evaluated value as a double + */ + double evaluate(Context context); + + /** + * Creates a MathValue based on a mathematical expression. + * + * @param expression the mathematical expression to evaluate + * @param the type of the holder object for the context + * @return a MathValue instance representing the given expression + */ + static MathValue expression(String expression) { + return new ExpressionMathValueImpl<>(expression); + } + + /** + * Creates a MathValue based on a plain numerical value. + * + * @param value the numerical value to represent + * @param the type of the holder object for the context + * @return a MathValue instance representing the given plain value + */ + static MathValue plain(double value) { + return new PlainMathValueImpl<>(value); + } + + static MathValue ranged(String value) { + return new RangedMathValueImpl<>(value); + } + + /** + * Automatically creates a MathValue based on the given object. + * If the object is a String, it is treated as a mathematical expression. + * If the object is a numerical type (Double, Integer, Long, Float), it is treated as a plain value. + * + * @param o the object to evaluate and create a MathValue from + * @param the type of the holder object for the context + * @return a MathValue instance representing the given object, either as an expression or a plain value + * @throws IllegalArgumentException if the object type is not supported + */ + static MathValue auto(Object o) { + if (o instanceof String s) { + if (s.contains("~")) { + return ranged(s); + } + try { + return plain(Double.parseDouble(s)); + } catch (NumberFormatException e) { + return expression(s); + } + } else if (o instanceof Number n) { + return plain(n.doubleValue()); + } + throw new IllegalArgumentException("Unsupported type: " + o.getClass()); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/PlaceholderTextValueImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/PlaceholderTextValueImpl.java new file mode 100644 index 00000000..6d3dea9a --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/PlaceholderTextValueImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.misc.value; + +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.misc.placeholder.BukkitPlaceholderManager; +import org.bukkit.OfflinePlayer; + +import java.util.Map; + +public class PlaceholderTextValueImpl implements TextValue { + + private final String raw; + + public PlaceholderTextValueImpl(String raw) { + this.raw = raw; + } + + @Override + public String render(Context context) { + Map replacements = context.placeholderMap(); + String text; + if (context.getHolder() instanceof OfflinePlayer player) text = BukkitPlaceholderManager.getInstance().parse(player, raw, replacements); + else text = BukkitPlaceholderManager.getInstance().parse(null, raw, replacements); + return text; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/PlainValue.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/PlainMathValueImpl.java similarity index 71% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/PlainValue.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/PlainMathValueImpl.java index 8c22a4c5..9fd0f4de 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/PlainValue.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/PlainMathValueImpl.java @@ -15,23 +15,20 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.misc.value; +package net.momirealms.customfishing.api.mechanic.misc.value; -import net.momirealms.customfishing.api.mechanic.misc.Value; -import org.bukkit.entity.Player; +import net.momirealms.customfishing.api.mechanic.context.Context; -import java.util.Map; - -public class PlainValue implements Value { +public class PlainMathValueImpl implements MathValue { private final double value; - public PlainValue(double value) { + public PlainMathValueImpl(double value) { this.value = value; } @Override - public double get(Player player, Map values) { + public double evaluate(Context context) { return value; } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameSettings.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/PlainTextValueImpl.java similarity index 64% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameSettings.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/PlainTextValueImpl.java index 98bea519..6a3907e9 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameSettings.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/PlainTextValueImpl.java @@ -15,23 +15,20 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.mechanic.game; +package net.momirealms.customfishing.api.mechanic.misc.value; -public class GameSettings { +import net.momirealms.customfishing.api.mechanic.context.Context; - private final double time; - private final int difficulty; +public class PlainTextValueImpl implements TextValue { - public GameSettings(double time, int difficulty) { - this.time = time; - this.difficulty = difficulty; + private final String raw; + + public PlainTextValueImpl(String raw) { + this.raw = raw; } - public double getTime() { - return time; - } - - public int getDifficulty() { - return difficulty; + @Override + public String render(Context context) { + return raw; } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/RangedMathValueImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/RangedMathValueImpl.java new file mode 100644 index 00000000..dde3dd34 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/RangedMathValueImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.misc.value; + +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.util.RandomUtils; + +public class RangedMathValueImpl implements MathValue { + + private final double min; + private final double max; + + public RangedMathValueImpl(String value) { + String[] split = value.split("~"); + if (split.length != 2) { + throw new IllegalArgumentException("Correct ranged format `a~b`"); + } + double min = Double.parseDouble(split[0]); + double max = Double.parseDouble(split[1]); + if (min > max) { + double temp = max; + max = min; + min = temp; + } + this.min = min; + this.max = max; + } + + @Override + public double evaluate(Context context) { + return RandomUtils.generateRandomDouble(min, max); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/TextValue.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/TextValue.java new file mode 100644 index 00000000..1640ccd3 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/value/TextValue.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.misc.value; + +import net.momirealms.customfishing.api.mechanic.context.Context; + +import java.util.regex.Pattern; + +/** + * The TextValue interface represents a text value that can be rendered + * within a specific context. This interface allows for the rendering of + * placeholder-based or plain text values in the context of custom fishing mechanics. + * + * @param the type of the holder object for the context + */ +public interface TextValue { + + Pattern pattern = Pattern.compile("\\{[^{}]+}"); + + /** + * Renders the text value within the given context. + * + * @param context the context in which the text value is rendered + * @return the rendered text as a String + */ + String render(Context context); + + /** + * Creates a TextValue based on a placeholder text. + * Placeholders can be dynamically replaced with context-specific values. + * + * @param text the placeholder text to render + * @param the type of the holder object for the context + * @return a TextValue instance representing the given placeholder text + */ + static TextValue placeholder(String text) { + return new PlaceholderTextValueImpl<>(text); + } + + /** + * Creates a TextValue based on plain text. + * + * @param text the plain text to render + * @param the type of the holder object for the context + * @return a TextValue instance representing the given plain text + */ + static TextValue plain(String text) { + return new PlainTextValueImpl<>(text); + } + + /** + * Automatically creates a TextValue based on the given argument. + * If the argument contains placeholders (detected by a regex pattern), + * a PlaceholderTextValueImpl instance is created. Otherwise, a PlainTextValueImpl + * instance is created. + * + * @param arg the text to evaluate and create a TextValue from + * @param the type of the holder object for the context + * @return a TextValue instance representing the given text, either as a placeholder or plain text + */ + static TextValue auto(String arg) { + if (pattern.matcher(arg).find()) + return placeholder(arg); + else + return plain(arg); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/ConditionalElement.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/ConditionalElement.java new file mode 100644 index 00000000..eeb6e829 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/ConditionalElement.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.requirement; + +import java.util.Map; + +public class ConditionalElement { + + private final E element; + private final Map> subElements; + private final Requirement[] requirements; + + public ConditionalElement(E element, Map> subElements, Requirement[] requirements) { + this.element = element; + this.subElements = subElements; + this.requirements = requirements; + } + + public E getElement() { + return element; + } + + public Requirement[] getRequirements() { + return requirements; + } + + public Map> getSubElements() { + return subElements; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/EmptyRequirement.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/EmptyRequirement.java similarity index 68% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/EmptyRequirement.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/EmptyRequirement.java index f01645be..e82217fc 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/EmptyRequirement.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/EmptyRequirement.java @@ -15,20 +15,20 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.requirement; +package net.momirealms.customfishing.api.mechanic.requirement; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.requirement.Requirement; +import net.momirealms.customfishing.api.mechanic.context.Context; +import org.bukkit.entity.Player; /** * Represents an empty requirement that always returns true when checking conditions. */ -public class EmptyRequirement implements Requirement { +public class EmptyRequirement implements Requirement { - public static EmptyRequirement instance = new EmptyRequirement(); + public static final EmptyRequirement INSTANCE = new EmptyRequirement(); @Override - public boolean isConditionMet(Condition condition) { + public boolean isSatisfied(Context context) { return true; } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/Requirement.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/Requirement.java index 3fac8c48..40d635fb 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/Requirement.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/Requirement.java @@ -17,15 +17,21 @@ package net.momirealms.customfishing.api.mechanic.requirement; -import net.momirealms.customfishing.api.mechanic.condition.Condition; +import net.momirealms.customfishing.api.mechanic.context.Context; -public interface Requirement { +/** + * Interface representing a requirement that must be met in the custom fishing API. + * This can be used to define conditions that need to be satisfied within a given context. + * + * @param the type parameter for the context + */ +public interface Requirement { /** - * Is condition met the requirement + * Evaluates whether the requirement is met within the given context. * - * @param condition condition - * @return meet or not + * @param context the context in which the requirement is evaluated + * @return true if the requirement is met, false otherwise */ - boolean isConditionMet(Condition condition); -} + boolean isSatisfied(Context context); +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementExpansion.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementExpansion.java index 11d04713..0e274c47 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementExpansion.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementExpansion.java @@ -21,7 +21,7 @@ package net.momirealms.customfishing.api.mechanic.requirement; * An abstract class representing a requirement expansion * Requirement expansions are used to define custom requirements for various functionalities. */ -public abstract class RequirementExpansion { +public abstract class RequirementExpansion { /** * Get the version of this requirement expansion. @@ -49,5 +49,5 @@ public abstract class RequirementExpansion { * * @return The requirement factory. */ - public abstract RequirementFactory getRequirementFactory(); + public abstract RequirementFactory getRequirementFactory(); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementFactory.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementFactory.java index adbb1030..a3657b15 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementFactory.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementFactory.java @@ -22,19 +22,21 @@ import net.momirealms.customfishing.api.mechanic.action.Action; import java.util.List; /** - * An interface for a requirement factory that builds requirements. + * Interface representing a factory for creating requirements. + * + * @param the type of object that the requirement will operate on */ -public interface RequirementFactory { +public interface RequirementFactory { /** - * Build a requirement with the given arguments, not met actions, and check action flag. + * Build a requirement with the given arguments, not satisfied actions, and check run actions flag. * - * @param args The arguments used to build the requirement. - * @param notMetActions Actions to be triggered when the requirement is not met (can be null). - * @param advanced Flag indicating whether to check the action when building the requirement. + * @param args The arguments used to build the requirement. + * @param notSatisfiedActions Actions to be triggered when the requirement is not met (can be null). + * @param runActions Flag indicating whether to run the action if the requirement is not met. * @return The built requirement. */ - Requirement build(Object args, List notMetActions, boolean advanced); + Requirement process(Object args, List> notSatisfiedActions, boolean runActions); /** * Build a requirement with the given arguments. @@ -42,7 +44,7 @@ public interface RequirementFactory { * @param args The arguments used to build the requirement. * @return The built requirement. */ - default Requirement build(Object args) { - return build(args, null, false); + default Requirement process(Object args) { + return process(args, List.of(), false); } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementManager.java new file mode 100644 index 00000000..a6d6eedc --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/requirement/RequirementManager.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.requirement; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * The RequirementManager interface manages custom requirement types and provides methods for handling requirements. + * + * @param the type of the context in which the requirements are evaluated. + */ +public interface RequirementManager extends Reloadable { + + /** + * 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(@NotNull String type, @NotNull 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(@NotNull String type); + + /** + * Checks if a requirement type is registered. + * + * @param type The type identifier of the requirement. + * @return True if the requirement type is registered, otherwise false. + */ + boolean hasRequirement(@NotNull 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(@NotNull String type); + + /** + * Retrieves an array of requirements based on a configuration section. + * + * @param section The configuration section containing requirement definitions. + * @param runActions A flag indicating whether to use advanced requirements. + * @return An array of Requirement objects based on the configuration section. + */ + @NotNull + Requirement[] parseRequirements(Section section, boolean runActions); + + /** + * Retrieves a Requirement object based on a configuration section and advanced flag. + * + * @param section The configuration section containing requirement definitions. + * @param runActions 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 parseRequirement(@NotNull Section section, boolean runActions); + + /** + * 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. + * + * @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 parseRequirement(@NotNull String type, @NotNull Object value); + + /** + * Checks if all requirements in the provided array are satisfied within the given context. + * + * @param context The context in which the requirements are evaluated. + * @param requirements An array of requirements to check. + * @return True if all requirements are satisfied, otherwise false. + */ + static boolean isSatisfied(Context context, @Nullable Requirement[] requirements) { + if (requirements == null) return true; + for (Requirement requirement : requirements) { + if (!requirement.isSatisfied(context)) { + return false; + } + } + return true; + } + + static boolean isSatisfied(Context context, @Nullable List> requirements) { + if (requirements == null) return true; + for (Requirement requirement : requirements) { + if (!requirement.isSatisfied(context)) { + return false; + } + } + return true; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/FishingStatistics.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/FishingStatistics.java new file mode 100644 index 00000000..c21bf6cd --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/FishingStatistics.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.statistic; + +import net.momirealms.customfishing.common.util.Pair; + +import java.util.Map; + +/** + * The FishingStatistics interface represents statistics related to fishing activities. + * It provides methods to retrieve and manipulate statistics such as the amount of fish caught and the maximum size of fish. + */ +public interface FishingStatistics { + + /** + * Retrieves the total amount of fish caught. + * + * @return the total amount of fish caught. + */ + int amountOfFishCaught(); + + /** + * Sets the total amount of fish caught. + * + * @param amountOfFishCaught the new total amount of fish caught. + */ + void amountOfFishCaught(int amountOfFishCaught); + + /** + * Retrieves the amount of fish caught with the specified ID. + * + * @param id the ID of the fish. + * @return the amount of fish caught with the specified ID. -1 if not exist. + */ + int getAmount(String id); + + /** + * Adds the specified amount to the fish caught with the specified ID and returns the updated amount. + * + * @param id the ID of the fish. + * @param amount the amount to add. + * @return a Pair containing the previous amount and the updated amount. + */ + Pair addAmount(String id, int amount); + + /** + * Sets the amount of fish caught with the specified ID. + * + * @param id the ID of the fish. + * @param amount the new amount to set. + */ + void setAmount(String id, int amount); + + /** + * Retrieves the maximum size of the fish with the specified ID. + * + * @param id the ID of the fish. + * @return the maximum size of the fish with the specified ID. -1f if not exist. + */ + float getMaxSize(String id); + + /** + * Sets the maximum size of the fish with the specified ID. + * + * @param id the ID of the fish. + * @param maxSize the new maximum size to set. + */ + void setMaxSize(String id, float maxSize); + + /** + * Updates the maximum size of the fish with the specified ID and returns true if successful, false otherwise. + * + * @param id the ID of the fish. + * @param newSize the new maximum size. + * @return true if the update is successful, false otherwise. + */ + boolean updateSize(String id, float newSize); + + /** + * Resets the fishing statistics, clearing all recorded data. + */ + void reset(); + + /** + * Retrieves the map containing the amounts of fish caught. + * + * @return the map containing the amounts of fish caught. + */ + Map amountMap(); + + /** + * Retrieves the map containing the maximum sizes of fish. + * + * @return the map containing the maximum sizes of fish. + */ + Map sizeMap(); + + /** + * Creates a new Builder instance for constructing FishingStatistics objects. + * + * @return a new Builder instance. + */ + static Builder builder() { + return new FishingStatisticsImpl.BuilderImpl(); + } + + /** + * The Builder interface provides a fluent API for constructing FishingStatistics instances. + */ + interface Builder { + + /** + * Sets the map containing the amounts of fish caught. + * + * @param amountMap the map containing the amounts of fish caught. + * @return the Builder instance. + */ + Builder amountMap(Map amountMap); + + /** + * Sets the map containing the maximum sizes of fish. + * + * @param sizeMap the map containing the maximum sizes of fish. + * @return the Builder instance. + */ + Builder sizeMap(Map sizeMap); + + /** + * Builds and returns the FishingStatistics instance. + * + * @return the constructed FishingStatistics instance. + */ + FishingStatistics build(); + } + + enum Type { + MAX_SIZE, + AMOUNT_OF_FISH_CAUGHT + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/FishingStatisticsImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/FishingStatisticsImpl.java new file mode 100644 index 00000000..5dcbc2ec --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/FishingStatisticsImpl.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.statistic; + +import net.momirealms.customfishing.common.util.Pair; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class FishingStatisticsImpl implements FishingStatistics { + + private int amountOfFishCaught; + private final Map amountMap; + private final Map sizeMap; + + public FishingStatisticsImpl(HashMap amountMap, HashMap sizeMap) { + this.amountMap = Collections.synchronizedMap(amountMap); + this.sizeMap = Collections.synchronizedMap(sizeMap); + this.amountOfFishCaught = amountMap.values().stream().mapToInt(Integer::intValue).sum(); + } + + @Override + public int amountOfFishCaught() { + return amountOfFishCaught; + } + + @Override + public void amountOfFishCaught(int amountOfFishCaught) { + this.amountOfFishCaught = amountOfFishCaught; + } + + @Override + public int getAmount(String id) { + return amountMap.getOrDefault(id, -1); + } + + @Override + public Pair addAmount(String id, int amount) { + if (amount <= 0) return Pair.of(-1, -1); + int previous = amountMap.getOrDefault(id, 0); + amountMap.put(id, previous + amount); + amountOfFishCaught += amount; + return Pair.of(previous, previous + amount); + } + + @Override + public void setAmount(String id, int amount) { + if (amount < 0) amount = 0; + int previous = amountMap.getOrDefault(id, 0); + int delta = amount - previous; + this.amountOfFishCaught += delta; + amountMap.put(id, amount); + } + + @Override + public float getMaxSize(String id) { + return sizeMap.getOrDefault(id, -1f); + } + + @Override + public void setMaxSize(String id, float maxSize) { + if (maxSize < 0) maxSize = 0; + sizeMap.put(id, maxSize); + } + + @Override + public boolean updateSize(String id, float newSize) { + if (newSize <= 0) return false; + float previous = sizeMap.getOrDefault(id, 0f); + if (previous >= newSize) return false; + sizeMap.put(id, newSize); + return true; + } + + @Override + public void reset() { + this.sizeMap.clear(); + this.amountMap.clear(); + this.amountOfFishCaught = 0; + } + + @Override + public Map amountMap() { + return amountMap; + } + + @Override + public Map sizeMap() { + return sizeMap; + } + + public static class BuilderImpl implements Builder { + private final HashMap amountMap = new HashMap<>(); + private final HashMap sizeMap = new HashMap<>(); + @Override + public Builder amountMap(Map amountMap) { + this.amountMap.putAll(amountMap); + return this; + } + @Override + public Builder sizeMap(Map sizeMap) { + this.sizeMap.putAll(sizeMap); + return this; + } + @Override + public FishingStatistics build() { + return new FishingStatisticsImpl(amountMap, sizeMap); + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/Statistics.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/Statistics.java deleted file mode 100644 index 081fb28e..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/Statistics.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.mechanic.statistic; - -import net.momirealms.customfishing.api.data.StatisticData; -import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.loot.Loot; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Represents a statistics system for tracking loot and catch amounts. - */ -public class Statistics { - - private final ConcurrentHashMap statisticMap; - private final ConcurrentHashMap sizeMap; - private int total; - - /** - * Creates a new instance of Statistics based on provided statistic data. - * - * @param statisticData The initial statistic data. - */ - public Statistics(StatisticData statisticData) { - this.statisticMap = new ConcurrentHashMap<>(statisticData.amountMap); - this.sizeMap = new ConcurrentHashMap<>(statisticData.sizeMap); - this.total = statisticMap.values().stream().mapToInt(Integer::intValue).sum(); - } - - /** - * Adds an amount of loot to the statistics. - * - * @param loot The loot item. - * @param condition The condition associated with the loot. - * @param amount The amount of loot to add. - */ - public synchronized void addLootAmount(Loot loot, Condition condition, int amount) { - if (amount < 1) { - return; - } - if (amount == 1) { - addSingleLootAmount(loot, condition); - return; - } - Integer previous = statisticMap.get(loot.getStatisticKey().getAmountKey()); - if (previous == null) previous = 0; - int after = previous + amount; - statisticMap.put(loot.getStatisticKey().getAmountKey(), after); - total += amount; - doSuccessTimesAction(previous, after, condition, loot); - } - - /** - * Performs actions associated with the success times of acquiring loot. - * - * @param previous The previous success times. - * @param after The updated success times. - * @param condition The condition associated with the loot. - * @param loot The loot item. - */ - private void doSuccessTimesAction(Integer previous, int after, Condition condition, Loot loot) { - HashMap actionMap = loot.getSuccessTimesActionMap(); - if (actionMap != null) { - for (Map.Entry entry : actionMap.entrySet()) { - if (entry.getKey() > previous && entry.getKey() <= after) { - for (Action action : entry.getValue()) { - action.trigger(condition); - } - } - } - } - } - - public boolean setSizeIfHigher(String loot, float size) { - float previous = sizeMap.getOrDefault(loot, 0f); - if (previous >= size) return false; - sizeMap.put(loot, size); - return true; - } - - /** - * Adds a single loot amount to the statistics. - * - * @param loot The loot item. - * @param condition The condition associated with the loot. - */ - private void addSingleLootAmount(Loot loot, Condition condition) { - Integer previous = statisticMap.get(loot.getID()); - if (previous == null) previous = 0; - int after = previous + 1; - statisticMap.put(loot.getID(), after); - total += 1; - Action[] actions = loot.getSuccessTimesActionMap().get(after); - if (actions != null) - for (Action action : actions) { - action.trigger(condition); - } - } - - /** - * Gets the amount of a specific loot item in the statistics. - * - * @param key The key of the loot item. - * @return The amount of the specified loot item. - */ - public int getLootAmount(String key) { - return statisticMap.getOrDefault(key, 0); - } - - public float getSizeRecord(String key) { - return sizeMap.getOrDefault(key, 0f); - } - - /** - * Resets the statistics data. - */ - public void reset() { - statisticMap.clear(); - total = 0; - } - - /** - * Gets the statistic map containing loot item keys and their respective amounts. - * - * @return The statistic map. - */ - public Map getStatisticMap() { - return statisticMap; - } - - public ConcurrentHashMap getSizeMap() { - return sizeMap; - } - - /** - * Sets data for a specific key in the statistics. - * - * @param key The key to set data for. - * @param value The value to set. - */ - public void setData(String key, int value) { - if (value <= 0) { - statisticMap.remove(key); - return; - } - statisticMap.put(key, value); - } - - /** - * Gets the total catch amount across all loot items. - * - * @return The total catch amount. - */ - public int getTotalCatchAmount() { - return total; - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/StatisticsKey.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/StatisticsKey.java deleted file mode 100644 index 6d3c1b16..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/StatisticsKey.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.momirealms.customfishing.api.mechanic.statistic; - -public class StatisticsKey { - - private final String amountKey; - private final String sizeKey; - - public StatisticsKey(String amountKey, String sizeKey) { - this.amountKey = amountKey; - this.sizeKey = sizeKey; - } - - public String getAmountKey() { - return amountKey; - } - - public String getSizeKey() { - return sizeKey; - } -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/StatisticsKeys.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/StatisticsKeys.java new file mode 100644 index 00000000..b79647b2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/StatisticsKeys.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.statistic; + +public record StatisticsKeys(String amountKey, String sizeKey) { +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockSettings.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/StatisticsManager.java similarity index 69% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockSettings.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/StatisticsManager.java index 657d6b45..3d044bcf 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockSettings.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/StatisticsManager.java @@ -15,20 +15,15 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.mechanic.block; +package net.momirealms.customfishing.api.mechanic.statistic; + +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.jetbrains.annotations.NotNull; import java.util.List; -public interface BlockSettings { - String getBlockID(); +public interface StatisticsManager extends Reloadable { - List getDataModifier(); - - List getStateModifierList(); - - boolean isPersist(); - - double getHorizontalVector(); - - double getVerticalVector(); + @NotNull + List getCategoryMembers(String key); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemConfig.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemConfig.java index a19ef39c..e6bed212 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemConfig.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemConfig.java @@ -17,181 +17,43 @@ package net.momirealms.customfishing.api.mechanic.totem; -import net.momirealms.customfishing.api.mechanic.requirement.Requirement; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; import net.momirealms.customfishing.api.mechanic.totem.block.TotemBlock; import org.bukkit.Location; +import org.bukkit.entity.Player; -/** - * This class represents the configuration for a totem. - * It defines various settings and properties for the totem. - */ -public class TotemConfig { +public interface TotemConfig { - private String key; - private TotemModel[] totemModels; - private TotemParticle[] particleSettings; - private Requirement[] requirements; - private double radius; - private int duration; + TotemModel[] totemModels(); - /** - * Get the array of totem models that define the totem's pattern. - * - * @return An array of TotemModel objects. - */ - public TotemModel[] getTotemModels() { - return totemModels; + String id(); + + boolean isRightPattern(Location location); + + TotemParticle[] particleSettings(); + + MathValue radius(); + + MathValue duration(); + + TotemBlock[] totemCore(); + + static Builder builder() { + return new TotemConfigImpl.BuilderImpl(); } - /** - * Get the array of requirements for totem activation. - * - * @return An array of Requirement objects. - */ - public Requirement[] getRequirements() { - return requirements; - } + interface Builder { - /** - * Get the unique key associated with this totem configuration. - * - * @return The unique key as a string. - */ - public String getKey() { - return key; - } + Builder id(String id); - /** - * Check if the provided location matches any of the totem model patterns. - * - * @param location The location to check. - * @return True if the location matches a totem model pattern, false otherwise. - */ - public boolean isRightPattern(Location location) { - for (TotemModel totemModel : totemModels) { - if (totemModel.isPatternSatisfied(location)) { - return true; - } - } - return false; - } + Builder totemModels(TotemModel[] totemModels); - /** - * Get the array of particle settings for the totem's visual effects. - * - * @return An array of TotemParticle objects. - */ - public TotemParticle[] getParticleSettings() { - return particleSettings; - } + Builder particleSettings(TotemParticle[] particleSettings); - /** - * Get the activation radius of the totem. - * - * @return The activation radius as a double. - */ - public double getRadius() { - return radius; - } + Builder radius(MathValue radius); - /** - * Get the duration of the totem's effect when activated. - * - * @return The duration in seconds as an integer. - */ - public int getDuration() { - return duration; - } + Builder duration(MathValue duration); - /** - * Get the totem core associated with the first totem model. - * This is used for some internal functionality. - * - * @return An array of TotemBlock objects representing the totem core. - */ - public TotemBlock[] getTotemCore() { - return totemModels[0].getTotemCore(); - } - - public static Builder builder(String key) { - return new Builder(key); - } - - /** - * This class represents a builder for creating instances of TotemConfig. - * It allows for the convenient construction of TotemConfig objects with various settings. - */ - public static class Builder { - - private final TotemConfig config; - - public Builder(String key) { - this.config = new TotemConfig(); - this.config.key = key; - } - - /** - * Sets the totem models for the TotemConfig being built. - * - * @param totemModels An array of TotemModel objects representing different totem models. - * @return The builder instance to allow for method chaining. - */ - public Builder setTotemModels(TotemModel[] totemModels) { - config.totemModels = totemModels; - return this; - } - - /** - * Sets the particle settings for the TotemConfig being built. - * - * @param particleSettings An array of TotemParticle objects representing particle settings. - * @return The builder instance to allow for method chaining. - */ - public Builder setParticleSettings(TotemParticle[] particleSettings) { - config.particleSettings = particleSettings; - return this; - } - - /** - * Sets the requirements for the TotemConfig being built. - * - * @param requirements An array of Requirement objects representing activation requirements. - * @return The builder instance to allow for method chaining. - */ - public Builder setRequirements(Requirement[] requirements) { - config.requirements = requirements; - return this; - } - - /** - * Sets the radius for the TotemConfig being built. - * - * @param radius The activation radius for the totem. - * @return The builder instance to allow for method chaining. - */ - public Builder setRadius(double radius) { - config.radius = radius; - return this; - } - - /** - * Sets the duration for the TotemConfig being built. - * - * @param duration The duration of the totem's effect. - * @return The builder instance to allow for method chaining. - */ - public Builder setDuration(int duration) { - config.duration = duration; - return this; - } - - /** - * Builds and returns the finalized TotemConfig object. - * - * @return The constructed TotemConfig object. - */ - public TotemConfig build() { - return config; - } + TotemConfig build(); } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemConfigImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemConfigImpl.java new file mode 100644 index 00000000..a73000c1 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemConfigImpl.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.totem; + +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import net.momirealms.customfishing.api.mechanic.totem.block.TotemBlock; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import static java.util.Objects.requireNonNull; + +public class TotemConfigImpl implements TotemConfig { + + private final String id; + private final TotemModel[] totemModels; + private final TotemParticle[] particleSettings; + private final MathValue radius; + private final MathValue duration; + + public TotemConfigImpl(String id, TotemModel[] totemModels, TotemParticle[] particleSettings, MathValue radius, MathValue duration) { + this.id = id; + this.totemModels = totemModels; + this.particleSettings = particleSettings; + this.radius = radius; + this.duration = duration; + } + + @Override + public TotemModel[] totemModels() { + return totemModels; + } + + @Override + public String id() { + return id; + } + + @Override + public boolean isRightPattern(Location location) { + for (TotemModel totemModel : totemModels) { + if (totemModel.isPatternSatisfied(location)) { + return true; + } + } + return false; + } + + @Override + public TotemParticle[] particleSettings() { + return particleSettings; + } + + @Override + public MathValue radius() { + return radius; + } + + @Override + public MathValue duration() { + return duration; + } + + @Override + public TotemBlock[] totemCore() { + return totemModels[0].getTotemCore(); + } + + public static class BuilderImpl implements Builder { + private String id; + private TotemModel[] totemModels; + private TotemParticle[] particleSettings; + private MathValue radius; + private MathValue duration; + @Override + public Builder id(String id) { + this.id = id; + return this; + } + @Override + public Builder totemModels(TotemModel[] totemModels) { + this.totemModels = totemModels; + return this; + } + @Override + public Builder particleSettings(TotemParticle[] particleSettings) { + this.particleSettings = particleSettings; + return this; + } + @Override + public Builder radius(MathValue radius) { + this.radius = radius; + return this; + } + @Override + public Builder duration(MathValue duration) { + this.duration = duration; + return this; + } + @Override + public TotemConfig build() { + return new TotemConfigImpl(requireNonNull(id), requireNonNull(totemModels), particleSettings, radius, duration); + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemManager.java new file mode 100644 index 00000000..3b11f6aa --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemManager.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.totem; + +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.bukkit.Location; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Optional; + +public interface TotemManager extends Reloadable { + + Collection getActivatedTotems(Location location); + + boolean registerTotem(TotemConfig totem); + + @NotNull + Optional getTotem(String id); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemParticle.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemParticle.java index 0321992c..71ec13ef 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemParticle.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/TotemParticle.java @@ -1,6 +1,23 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package net.momirealms.customfishing.api.mechanic.totem; -import net.momirealms.customfishing.api.scheduler.CancellableTask; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; import org.bukkit.Location; public interface TotemParticle { @@ -12,5 +29,5 @@ public interface TotemParticle { * @param radius totem radius * @return cancellable task */ - CancellableTask start(Location location, double radius); -} \ No newline at end of file + SchedulerTask start(Location location, double radius); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/block/type/EqualType.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/block/type/EqualType.java index 8323aac5..dfed289b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/block/type/EqualType.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/totem/block/type/EqualType.java @@ -17,7 +17,7 @@ package net.momirealms.customfishing.api.mechanic.totem.block.type; -import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; import org.bukkit.block.Block; import java.io.Serializable; @@ -41,7 +41,7 @@ public class EqualType implements TypeCondition, Serializable { */ @Override public boolean isMet(Block type) { - return this.type.equals(CustomFishingPlugin.get().getBlockManager().getAnyPluginBlockID(type)); + return this.type.equals(BukkitCustomFishingPlugin.getInstance().getBlockManager().getBlockID(type)); } /** diff --git a/api/src/main/java/net/momirealms/customfishing/api/scheduler/Scheduler.java b/api/src/main/java/net/momirealms/customfishing/api/scheduler/Scheduler.java deleted file mode 100644 index 515a4c51..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/scheduler/Scheduler.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.scheduler; - -import org.bukkit.Location; - -import java.util.concurrent.TimeUnit; - -public interface Scheduler { - - /** - * 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 runTaskSync(Runnable runnable, Location location); - - /** - * 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); - - /** - * 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. - */ - CancellableTask runTaskAsyncLater(Runnable runnable, long delay, TimeUnit timeUnit); - - /** - * Runs a task asynchronously. - * - * @param runnable The task to run. - */ - void runTaskAsync(Runnable runnable); - - /** - * 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. - */ - CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay, TimeUnit 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. - */ - CancellableTask runTaskSyncLater(Runnable runnable, Location location, long 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. - */ - CancellableTask runTaskAsyncTimer(Runnable runnable, long delay, long period, TimeUnit timeUnit); -} diff --git a/api/src/main/java/net/momirealms/customfishing/api/storage/DataStorageProvider.java b/api/src/main/java/net/momirealms/customfishing/api/storage/DataStorageProvider.java new file mode 100644 index 00000000..339d8572 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/storage/DataStorageProvider.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.storage; + +import dev.dejvokep.boostedyaml.YamlDocument; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.api.storage.user.UserData; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public interface DataStorageProvider { + + void initialize(YamlDocument config); + + void disable(); + + StorageType getStorageType(); + + CompletableFuture> getPlayerData(UUID uuid, boolean lock); + + CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData, boolean unlock); + + CompletableFuture updateOrInsertPlayerData(UUID uuid, PlayerData playerData, boolean unlock); + + void updateManyPlayersData(Collection users, boolean unlock); + + void lockOrUnlockPlayerData(UUID uuid, boolean lock); + + Set getUniqueUsers(); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/storage/StorageManager.java b/api/src/main/java/net/momirealms/customfishing/api/storage/StorageManager.java new file mode 100644 index 00000000..bb4f72e6 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/storage/StorageManager.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.storage; + +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.api.storage.user.UserData; +import net.momirealms.customfishing.common.plugin.feature.Reloadable; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public interface StorageManager extends Reloadable { + + @NotNull + String getServerID(); + + @NotNull + Optional getOnlineUser(UUID uuid); + + @NotNull + Collection getOnlineUsers(); + + CompletableFuture> getOfflineUserData(UUID uuid, boolean lock); + + CompletableFuture saveUserData(UserData userData, boolean unlock); + + @NotNull + DataStorageProvider getDataSource(); + + boolean isRedisEnabled(); + + /** + * Converts PlayerData to bytes. + * + * @param data The PlayerData to be converted. + * @return The byte array representation of PlayerData. + */ + byte[] 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); + + /** + * 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/data/StorageType.java b/api/src/main/java/net/momirealms/customfishing/api/storage/StorageType.java similarity index 94% rename from api/src/main/java/net/momirealms/customfishing/api/data/StorageType.java rename to api/src/main/java/net/momirealms/customfishing/api/storage/StorageType.java index da6a3fe4..8a641db4 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/data/StorageType.java +++ b/api/src/main/java/net/momirealms/customfishing/api/storage/StorageType.java @@ -15,10 +15,9 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.data; +package net.momirealms.customfishing.api.storage; public enum StorageType { - JSON, YAML, H2, diff --git a/api/src/main/java/net/momirealms/customfishing/api/storage/data/EarningData.java b/api/src/main/java/net/momirealms/customfishing/api/storage/data/EarningData.java new file mode 100644 index 00000000..60b1e8e3 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/storage/data/EarningData.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.storage.data; + +import com.google.gson.annotations.SerializedName; + +import java.util.Calendar; + +/** + * The EarningData class holds data related to the earnings of a player from selling fish. + * It includes the total earnings and the date of the earnings record. + */ +public class EarningData { + + @SerializedName("earnings") + public double earnings; + @SerializedName("date") + public int date; + + /** + * Constructs a new EarningData instance with specified earnings and date. + * + * @param earnings the total earnings from fishing. + * @param date the date of the earnings record. + */ + public EarningData(double earnings, int date) { + this.earnings = earnings; + this.date = date; + this.refresh(); + } + + /** + * Creates an instance of EarningData with default values (zero earnings and date). + * + * @return a new instance of EarningData with default values. + */ + public static EarningData empty() { + return new EarningData(0d, 0); + } + + public EarningData copy() { + return new EarningData(earnings, date); + } + + public double earnings() { + return earnings; + } + + public int date() { + return date; + } + + public void refresh() { + Calendar calendar = Calendar.getInstance(); + int dat = (calendar.get(Calendar.MONTH) +1) * 100 + calendar.get(Calendar.DATE); + if (dat != date) { + date = dat; + earnings = 0; + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/data/InventoryData.java b/api/src/main/java/net/momirealms/customfishing/api/storage/data/InventoryData.java similarity index 62% rename from api/src/main/java/net/momirealms/customfishing/api/data/InventoryData.java rename to api/src/main/java/net/momirealms/customfishing/api/storage/data/InventoryData.java index ab8a3208..1d2b2118 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/data/InventoryData.java +++ b/api/src/main/java/net/momirealms/customfishing/api/storage/data/InventoryData.java @@ -15,22 +15,36 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.data; +package net.momirealms.customfishing.api.storage.data; import com.google.gson.annotations.SerializedName; +/** + * The InventoryData class holds data related to a player's fishing bag. + * It includes a serialized representation of the inventory and the size of the inventory. + */ public class InventoryData { @SerializedName("inventory") public String serialized; - @SerializedName("size") public int size; + /** + * Creates an instance of InventoryData with default values (empty inventory and size of 9). + * + * @return a new instance of InventoryData with default values. + */ public static InventoryData empty() { return new InventoryData("", 9); } + /** + * Constructs a new InventoryData instance with specified serialized inventory and size. + * + * @param serialized the serialized representation of the inventory. + * @param size the size of the inventory. + */ public InventoryData(String serialized, int size) { this.serialized = serialized; this.size = size; diff --git a/api/src/main/java/net/momirealms/customfishing/api/storage/data/PlayerData.java b/api/src/main/java/net/momirealms/customfishing/api/storage/data/PlayerData.java new file mode 100644 index 00000000..97dd6fac --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/storage/data/PlayerData.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.storage.data; + +import com.google.gson.annotations.SerializedName; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +import static java.util.Objects.requireNonNull; + +/** + * The PlayerData class holds data related to a player. + * It includes the player's name, their fishing statistics, inventory data, and earnings data. + */ +public class PlayerData { + + public static final String DEFAULT_NAME = ""; + public static final StatisticData DEFAULT_STATISTICS = StatisticData.empty(); + public static final InventoryData DEFAULT_BAG = InventoryData.empty(); + public static final EarningData DEFAULT_EARNING = EarningData.empty(); + + @SerializedName("name") + protected String name; + @SerializedName("stats") + protected StatisticData statisticsData; + @SerializedName("bag") + protected InventoryData bagData; + @SerializedName("trade") + protected EarningData earningData; + transient private UUID uuid; + transient private boolean locked; + + public PlayerData(UUID uuid, String name, StatisticData statisticsData, InventoryData bagData, EarningData earningData, boolean isLocked) { + this.name = name; + this.statisticsData = statisticsData; + this.bagData = bagData; + this.earningData = earningData; + this.locked = isLocked; + this.uuid = uuid; + } + + public static Builder builder() { + return new Builder(); + } + + public static PlayerData empty() { + return new Builder() + .bag(InventoryData.empty()) + .earnings(EarningData.empty()) + .statistics(StatisticData.empty()) + .uuid(new UUID(0, 0)) + .locked(false) + .build(); + } + + /** + * The Builder class provides a fluent API for constructing PlayerData instances. + */ + public static class Builder { + + private String name = DEFAULT_NAME; + private StatisticData statisticsData = DEFAULT_STATISTICS; + private InventoryData bagData = DEFAULT_BAG; + private EarningData earningData = DEFAULT_EARNING; + private boolean isLocked = false; + private UUID uuid; + + @NotNull + public Builder name(@NotNull String name) { + this.name = name; + return this; + } + + @NotNull + public Builder uuid(@NotNull UUID uuid) { + this.uuid = uuid; + return this; + } + + @NotNull + public Builder locked(boolean locked) { + this.isLocked = locked; + return this; + } + + @NotNull + public Builder statistics(@Nullable StatisticData statisticsData) { + this.statisticsData = statisticsData; + return this; + } + + @NotNull + public Builder bag(@Nullable InventoryData inventoryData) { + this.bagData = inventoryData; + return this; + } + + @NotNull + public Builder earnings(@Nullable EarningData earningData) { + this.earningData = earningData; + return this; + } + + @NotNull + public PlayerData build() { + return new PlayerData(requireNonNull(uuid), name, statisticsData, bagData, earningData, isLocked); + } + } + + /** + * Gets the statistics data for the player. + * + * @return the fishing statistics data. + */ + public StatisticData statistics() { + return statisticsData; + } + + /** + * Gets the bag data for the player. + * + * @return the bag data. + */ + public InventoryData bagData() { + return bagData; + } + + /** + * Gets the earnings data for the player. + * + * @return the earnings data. + */ + public EarningData earningData() { + return earningData; + } + + /** + * Gets the name of the player. + * + * @return the player's name. + */ + public String name() { + return name; + } + + /** + * Gets if the data is locked + * + * @return locked or not + */ + public boolean locked() { + return locked; + } + + public void locked(boolean locked) { + this.locked = locked; + } + + /** + * Gets the uuid + * + * @return uuid + */ + public UUID uuid() { + return uuid; + } + + public void uuid(UUID uuid) { + this.uuid = uuid; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/data/StatisticData.java b/api/src/main/java/net/momirealms/customfishing/api/storage/data/StatisticData.java similarity index 64% rename from api/src/main/java/net/momirealms/customfishing/api/data/StatisticData.java rename to api/src/main/java/net/momirealms/customfishing/api/storage/data/StatisticData.java index 80b4cef4..24ef0a7a 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/data/StatisticData.java +++ b/api/src/main/java/net/momirealms/customfishing/api/storage/data/StatisticData.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.data; +package net.momirealms.customfishing.api.storage.data; import com.google.gson.annotations.SerializedName; import org.jetbrains.annotations.NotNull; @@ -23,6 +23,10 @@ import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Map; +/** + * The StatisticData class stores fishing statistics including amounts and sizes + * of fish caught, represented as maps. + */ public class StatisticData { @SerializedName(value="amount", alternate={"map"}) @@ -31,16 +35,30 @@ public class StatisticData { @SerializedName("size") public Map sizeMap; - public StatisticData() { + /** + * Default constructor that initializes the sizeMap and amountMap as empty HashMaps. + */ + private StatisticData() { this.sizeMap = new HashMap<>(); this.amountMap = new HashMap<>(); } + /** + * Parameterized constructor that initializes the sizeMap and amountMap with provided values. + * + * @param amount a map containing the amount of each type of fish caught. + * @param size a map containing the size of each type of fish caught. + */ public StatisticData(@NotNull Map amount, @NotNull Map size) { this.amountMap = amount; this.sizeMap = size; } + /** + * Creates an instance of StatisticData with empty maps. + * + * @return a new instance of StatisticData with empty maps. + */ public static StatisticData empty() { return new StatisticData(); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/storage/user/UserData.java b/api/src/main/java/net/momirealms/customfishing/api/storage/user/UserData.java new file mode 100644 index 00000000..00aaa036 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/storage/user/UserData.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.storage.user; + +import net.momirealms.customfishing.api.mechanic.bag.FishingBagHolder; +import net.momirealms.customfishing.api.mechanic.statistic.FishingStatistics; +import net.momirealms.customfishing.api.storage.data.EarningData; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +public interface UserData { + + /** + * Get the username + * + * @return user name + */ + @NotNull + String name(); + + /** + * Get the user's uuid + * + * @return uuid + */ + @NotNull + UUID uuid(); + + /** + * Get the player instance if that player is online + * + * @return player + */ + @Nullable + Player player(); + + /** + * Get the fishing bag holder + * + * @return fishing bag holder + */ + @NotNull + FishingBagHolder holder(); + + /** + * Get the player's earning data + * + * @return earning data + */ + @NotNull + EarningData earningData(); + + /** + * Get the player's statistics + * + * @return statistics + */ + @NotNull + FishingStatistics statistics(); + + /** + * If the user is online on current server + * + * @return online or not + */ + boolean isOnline(); + + /** + * If the data is locked + * + * @return locked or not + */ + boolean isLocked(); + + /** + * Get the data in another minimized format that can be saved + * + * @return player data + */ + @NotNull + PlayerData toPlayerData(); + + static Builder builder() { + return new UserDataImpl.BuilderImpl(); + } + + interface Builder { + + /** + * Set the username for the UserData being built. + * + * @param name the username to set. + * @return the current Builder instance. + */ + Builder name(String name); + + /** + * Set the UUID for the UserData being built. + * + * @param uuid the UUID to set. + * @return the current Builder instance. + */ + Builder uuid(UUID uuid); + + /** + * Set the FishingBagHolder for the UserData being built. + * + * @param holder the FishingBagHolder to set. + * @return the current Builder instance. + */ + Builder holder(FishingBagHolder holder); + + /** + * Set the EarningData for the UserData being built. + * + * @param earningData the EarningData to set. + * @return the current Builder instance. + */ + Builder earningData(EarningData earningData); + + /** + * Set the FishingStatistics for the UserData being built. + * + * @param statistics the FishingStatistics to set. + * @return the current Builder instance. + */ + Builder statistics(FishingStatistics statistics); + + Builder locked(boolean isLocked); + + Builder data(PlayerData playerData); + + /** + * Build and return the UserData instance based on the current state of the Builder. + * + * @return the constructed UserData instance. + */ + UserData build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/storage/user/UserDataImpl.java b/api/src/main/java/net/momirealms/customfishing/api/storage/user/UserDataImpl.java new file mode 100644 index 00000000..fafb6652 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/storage/user/UserDataImpl.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.storage.user; + +import net.momirealms.customfishing.api.mechanic.bag.FishingBagHolder; +import net.momirealms.customfishing.api.mechanic.statistic.FishingStatistics; +import net.momirealms.customfishing.api.storage.data.EarningData; +import net.momirealms.customfishing.api.storage.data.InventoryData; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.api.storage.data.StatisticData; +import net.momirealms.customfishing.api.util.InventoryUtils; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; +import java.util.UUID; + +public class UserDataImpl implements UserData { + + private final String name; + private final UUID uuid; + private final FishingBagHolder holder; + private final EarningData earningData; + private final FishingStatistics statistics; + private final boolean isLocked; + + public UserDataImpl(String name, UUID uuid, FishingBagHolder holder, EarningData earningData, FishingStatistics statistics, boolean isLocked) { + this.name = name; + this.uuid = uuid; + this.holder = holder; + this.earningData = earningData; + this.statistics = statistics; + this.isLocked = isLocked; + } + + public static class BuilderImpl implements Builder { + private String name; + private UUID uuid; + private FishingBagHolder holder; + private EarningData earningData; + private FishingStatistics statistics; + private boolean isLocked; + @Override + public Builder name(String name) { + this.name = name; + return this; + } + @Override + public Builder uuid(UUID uuid) { + this.uuid = uuid; + return this; + } + @Override + public Builder holder(FishingBagHolder holder) { + this.holder = holder; + return this; + } + @Override + public Builder earningData(EarningData earningData) { + this.earningData = earningData.copy(); + return this; + } + @Override + public Builder statistics(FishingStatistics statistics) { + this.statistics = statistics; + return this; + } + @Override + public Builder locked(boolean isLocked) { + this.isLocked = isLocked; + return this; + } + @Override + public Builder data(PlayerData playerData) { + this.isLocked = playerData.locked(); + this.uuid = playerData.uuid(); + this.name = playerData.name(); + this.earningData = playerData.earningData().copy(); + this.holder = FishingBagHolder.create(playerData.uuid(), InventoryUtils.getInventoryItems(playerData.bagData().serialized), playerData.bagData().size); + this.statistics = FishingStatistics.builder().amountMap(playerData.statistics().amountMap).sizeMap(playerData.statistics().sizeMap).build(); + return this; + } + @Override + public UserData build() { + return new UserDataImpl(name, uuid, holder, earningData, statistics, isLocked); + } + } + + @NotNull + @Override + public String name() { + return name; + } + + @NotNull + @Override + public UUID uuid() { + return uuid; + } + + @Nullable + @Override + public Player player() { + return Bukkit.getPlayer(uuid); + } + + @NotNull + @Override + public FishingBagHolder holder() { + return holder; + } + + @NotNull + @Override + public EarningData earningData() { + return earningData; + } + + @NotNull + @Override + public FishingStatistics statistics() { + return statistics; + } + + @Override + public boolean isOnline() { + return Optional.ofNullable(Bukkit.getPlayer(uuid)).map(OfflinePlayer::isOnline).orElse(false); + } + + @Override + public boolean isLocked() { + return isLocked; + } + + @NotNull + @Override + public PlayerData toPlayerData() { + return PlayerData.builder() + .uuid(uuid) + .bag(new InventoryData(InventoryUtils.stacksToBase64(holder.getInventory().getStorageContents()), holder.getInventory().getSize())) + .earnings(earningData) + .statistics(new StatisticData(statistics.amountMap(), statistics.sizeMap())) + .name(name) + .build(); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/util/FontUtils.java b/api/src/main/java/net/momirealms/customfishing/api/util/EventUtils.java similarity index 56% rename from api/src/main/java/net/momirealms/customfishing/api/util/FontUtils.java rename to api/src/main/java/net/momirealms/customfishing/api/util/EventUtils.java index cd311e70..33a4d4ea 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/util/FontUtils.java +++ b/api/src/main/java/net/momirealms/customfishing/api/util/EventUtils.java @@ -17,23 +17,20 @@ package net.momirealms.customfishing.api.util; -/** - * Utility class for working with fonts in text. - */ -public class FontUtils { +import org.bukkit.Bukkit; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; - private FontUtils() { - throw new UnsupportedOperationException("This class cannot be instantiated"); +public class EventUtils { + + public static void fireAndForget(Event event) { + Bukkit.getPluginManager().callEvent(event); } - /** - * Surrounds the given text with a specified font tag. - * - * @param text The text to be surrounded with the font tag. - * @param font The font to use in the font tag. - * @return The input text surrounded by the font tag. - */ - public static String surroundWithFont(String text, String font) { - return "" + text + ""; + public static boolean fireAndCheckCancel(Event event) { + if (!(event instanceof Cancellable cancellable)) + throw new IllegalArgumentException("Only cancellable events are allowed here"); + Bukkit.getPluginManager().callEvent(event); + return cancellable.isCancelled(); } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/util/InventoryUtils.java b/api/src/main/java/net/momirealms/customfishing/api/util/InventoryUtils.java index bb1645b6..1b75dd2b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/util/InventoryUtils.java +++ b/api/src/main/java/net/momirealms/customfishing/api/util/InventoryUtils.java @@ -17,11 +17,6 @@ package net.momirealms.customfishing.api.util; -import net.kyori.adventure.text.Component; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.util.io.BukkitObjectInputStream; import org.bukkit.util.io.BukkitObjectOutputStream; @@ -32,8 +27,6 @@ import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; /** * Utility class for working with Bukkit Inventories and item stacks. @@ -41,65 +34,6 @@ import java.lang.reflect.Method; public class InventoryUtils { private InventoryUtils() { - throw new UnsupportedOperationException("This class cannot be instantiated"); - } - - /** - * Create a custom inventory with a specified size and title component. - * - * @param inventoryHolder The holder of the inventory. - * @param size The size of the inventory. - * @param component The title component of the inventory. - * @return The created Inventory instance. - */ - public static Inventory createInventory(InventoryHolder inventoryHolder, int size, Component component) { - try { - boolean isSpigot = CustomFishingPlugin.get().getVersionManager().isSpigot(); - Method createInvMethod = ReflectionUtils.bukkitClass.getMethod( - "createInventory", - InventoryHolder.class, - int.class, - isSpigot ? String.class : ReflectionUtils.componentClass - ); - return (Inventory) createInvMethod.invoke( - null, - inventoryHolder, - size, - isSpigot ? CustomFishingPlugin.get().getAdventure().componentToLegacy(component) : CustomFishingPlugin.get().getAdventure().shadedComponentToOriginalComponent(component) - ); - } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException exception) { - exception.printStackTrace(); - return null; - } - } - - /** - * Create a custom inventory with a specified type and title component. - * - * @param inventoryHolder The holder of the inventory. - * @param type The type of the inventory. - * @param component The title component of the inventory. - * @return The created Inventory instance. - */ - public static Inventory createInventory(InventoryHolder inventoryHolder, InventoryType type, Component component) { - try { - boolean isSpigot = CustomFishingPlugin.get().getVersionManager().isSpigot(); - Method createInvMethod = ReflectionUtils.bukkitClass.getMethod( - "createInventory", - InventoryHolder.class, - InventoryType.class, - isSpigot ? String.class : ReflectionUtils.componentClass - ); - return (Inventory) createInvMethod.invoke( - null, - inventoryHolder, - type, - isSpigot ? CustomFishingPlugin.get().getAdventure().componentToLegacy(component) : CustomFishingPlugin.get().getAdventure().shadedComponentToOriginalComponent(component) - ); - } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException exception) { - exception.printStackTrace(); - return null; - } } /** @@ -109,7 +43,7 @@ public class InventoryUtils { * @return The Base64-encoded string representing the serialized ItemStacks. */ public static @NotNull String stacksToBase64(ItemStack[] contents) { - if (contents.length == 0) { + if (contents == null || contents.length == 0) { return ""; } try { @@ -124,7 +58,7 @@ public class InventoryUtils { outputStream.close(); return Base64Coder.encodeLines(byteArr); } catch (IOException e) { - LogUtils.warn("Encoding error", e); + e.printStackTrace(); } return ""; } @@ -138,40 +72,29 @@ public class InventoryUtils { @Nullable public static ItemStack[] getInventoryItems(String base64) { ItemStack[] itemStacks = null; - try { - itemStacks = stacksFromBase64(base64); - } catch (IllegalArgumentException exception) { - exception.printStackTrace(); - } - return itemStacks; - } - - private static ItemStack[] stacksFromBase64(String data) { - if (data == null || data.equals("")) return new ItemStack[]{}; - + if (base64 == null || base64.isEmpty()) return new ItemStack[]{}; ByteArrayInputStream inputStream; try { - inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data)); - } catch (IllegalArgumentException e) { + inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(base64)); + } catch (IllegalArgumentException ignored) { return new ItemStack[]{}; } BukkitObjectInputStream dataInput = null; - ItemStack[] stacks = null; try { dataInput = new BukkitObjectInputStream(inputStream); - stacks = new ItemStack[dataInput.readInt()]; - } catch (IOException e) { - e.printStackTrace(); + itemStacks = new ItemStack[dataInput.readInt()]; + } catch (IOException ioException) { + ioException.printStackTrace(); } - if (stacks == null) return new ItemStack[]{}; - for (int i = 0; i < stacks.length; i++) { + if (itemStacks == null) return new ItemStack[]{}; + for (int i = 0; i < itemStacks.length; i++) { try { - stacks[i] = (ItemStack) dataInput.readObject(); + itemStacks[i] = (ItemStack) dataInput.readObject(); } catch (IOException | ClassNotFoundException | NullPointerException e) { try { dataInput.close(); - } catch (IOException exception) { - LogUtils.severe("Failed to read fishing bag data"); + } catch (IOException ioException) { + ioException.printStackTrace(); } return null; } @@ -180,6 +103,6 @@ public class InventoryUtils { dataInput.close(); } catch (IOException ignored) { } - return stacks; + return itemStacks; } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/util/LogUtils.java b/api/src/main/java/net/momirealms/customfishing/api/util/LogUtils.java deleted file mode 100644 index b99d4112..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/util/LogUtils.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.util; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import org.jetbrains.annotations.NotNull; - -import java.util.logging.Level; - -/** - * Utility class for logging messages with various log levels. - */ -public final class LogUtils { - - /** - * Log an informational message. - * - * @param message The message to log. - */ - public static void info(@NotNull String message) { - CustomFishingPlugin.getInstance().getLogger().info(message); - } - - /** - * Log a warning message. - * - * @param message The message to log. - */ - public static void warn(@NotNull String message) { - CustomFishingPlugin.getInstance().getLogger().warning(message); - } - - /** - * Log a severe error message. - * - * @param message The message to log. - */ - public static void severe(@NotNull String message) { - CustomFishingPlugin.getInstance().getLogger().severe(message); - } - - /** - * Log a warning message with a throwable exception. - * - * @param message The message to log. - * @param throwable The throwable exception to log. - */ - public static void warn(@NotNull String message, Throwable throwable) { - CustomFishingPlugin.getInstance().getLogger().log(Level.WARNING, message, throwable); - } - - /** - * Log a severe error message with a throwable exception. - * - * @param message The message to log. - * @param throwable The throwable exception to log. - */ - public static void severe(@NotNull String message, Throwable throwable) { - CustomFishingPlugin.getInstance().getLogger().log(Level.SEVERE, message, throwable); - } - - private LogUtils() { - throw new UnsupportedOperationException("This class cannot be instantiated"); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/MoonPhase.java b/api/src/main/java/net/momirealms/customfishing/api/util/MoonPhase.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customfishing/util/MoonPhase.java rename to api/src/main/java/net/momirealms/customfishing/api/util/MoonPhase.java index 25803379..338e2f72 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/MoonPhase.java +++ b/api/src/main/java/net/momirealms/customfishing/api/util/MoonPhase.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.util; +package net.momirealms.customfishing.api.util; import org.jetbrains.annotations.NotNull; diff --git a/api/src/main/java/net/momirealms/customfishing/api/util/OffsetUtils.java b/api/src/main/java/net/momirealms/customfishing/api/util/OffsetUtils.java index bfb0956e..55a1c4a8 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/util/OffsetUtils.java +++ b/api/src/main/java/net/momirealms/customfishing/api/util/OffsetUtils.java @@ -17,7 +17,9 @@ package net.momirealms.customfishing.api.util; -import org.bukkit.configuration.ConfigurationSection; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import org.jetbrains.annotations.ApiStatus; /** * Utility class for generating offset characters based on a font configuration. @@ -47,12 +49,8 @@ public class OffsetUtils { private static String positive_64; private static String positive_128; - /** - * Load font configuration from a given section. - * - * @param section The configuration section containing font settings. - */ - public static void loadConfig(ConfigurationSection section) { + @ApiStatus.Internal + public static void load(Section section) { if (section != null) { font = section.getString("font", "customfishing:offset_chars"); positive_1 = section.getString("1"); @@ -167,9 +165,9 @@ public class OffsetUtils { */ public static String getOffsetChars(int n) { if (n > 0) { - return "" + getShortestPosChars(n) + ""; + return AdventureHelper.surroundWithMiniMessageFont(getShortestPosChars(n), font); } else { - return "" + getShortestNegChars(-n) + ""; + return AdventureHelper.surroundWithMiniMessageFont(getShortestNegChars(-n), font); } } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/util/ReflectionUtils.java b/api/src/main/java/net/momirealms/customfishing/api/util/ReflectionUtils.java deleted file mode 100644 index 5283a8de..00000000 --- a/api/src/main/java/net/momirealms/customfishing/api/util/ReflectionUtils.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.api.util; - -import com.comphenix.protocol.utility.MinecraftReflection; -import net.momirealms.customfishing.api.CustomFishingPlugin; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public class ReflectionUtils { - - public static Object removeBossBarPacket; - public static Constructor progressConstructor; - public static Constructor updateConstructor; - public static Method iChatComponentMethod; - public static Method gsonDeserializeMethod; - public static Object gsonInstance; - public static Class componentClass; - public static Class bukkitClass; - - public static void load() { - if (CustomFishingPlugin.get().getVersionManager().isMojmap()) { - try { - Class bar = Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket"); - Field remove = bar.getDeclaredField("REMOVE_OPERATION"); - remove.setAccessible(true); - removeBossBarPacket = remove.get(null); - Class packetBossClassF = Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket$UpdateProgressOperation"); - progressConstructor = packetBossClassF.getDeclaredConstructor(float.class); - progressConstructor.setAccessible(true); - Class packetBossClassE = Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket$UpdateNameOperation"); - updateConstructor = packetBossClassE.getDeclaredConstructor(MinecraftReflection.getIChatBaseComponentClass()); - updateConstructor.setAccessible(true); - Class craftChatMessageClass = Class.forName("org.bukkit.craftbukkit.util.CraftChatMessage"); - iChatComponentMethod = craftChatMessageClass.getDeclaredMethod("fromJSON", String.class); - iChatComponentMethod.setAccessible(true); - } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException exception) { - LogUtils.severe("Error occurred when loading reflections", exception); - } - } else { - try { - Class bar = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutBoss"); - Field remove = bar.getDeclaredField("f"); - remove.setAccessible(true); - removeBossBarPacket = remove.get(null); - Class packetBossClassF = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutBoss$f"); - progressConstructor = packetBossClassF.getDeclaredConstructor(float.class); - progressConstructor.setAccessible(true); - Class packetBossClassE = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutBoss$e"); - updateConstructor = packetBossClassE.getDeclaredConstructor(MinecraftReflection.getIChatBaseComponentClass()); - updateConstructor.setAccessible(true); - iChatComponentMethod = MinecraftReflection.getChatSerializerClass().getMethod("a", String.class); - iChatComponentMethod.setAccessible(true); - } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException exception) { - LogUtils.severe("Error occurred when loading reflections", exception); - } - } - if (CustomFishingPlugin.get().getVersionManager().isSpigot()) return; - try { - componentClass = Class.forName("net;kyori;adventure;text;Component".replace(";", ".")); - bukkitClass = Class.forName("org;bukkit;Bukkit".replace(";", ".")); - Class gsonComponentSerializerClass = Class.forName("net;kyori;adventure;text;serializer;gson;GsonComponentSerializer".replace(";", ".")); - Class gsonComponentSerializerImplClass = Class.forName("net;kyori;adventure;text;serializer;gson;GsonComponentSerializerImpl".replace(";", ".")); - Method gsonMethod = gsonComponentSerializerClass.getMethod("gson"); - gsonInstance = gsonMethod.invoke(null); - gsonDeserializeMethod = gsonComponentSerializerImplClass.getMethod("deserialize", String.class); - gsonDeserializeMethod.setAccessible(true); - } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException exception) { - LogUtils.severe("Error occurred when loading reflections", exception); - } - } -} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/common/SimpleLocation.java b/api/src/main/java/net/momirealms/customfishing/api/util/SimpleLocation.java similarity index 80% rename from api/src/main/java/net/momirealms/customfishing/api/common/SimpleLocation.java rename to api/src/main/java/net/momirealms/customfishing/api/util/SimpleLocation.java index b14bec65..c83d3208 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/common/SimpleLocation.java +++ b/api/src/main/java/net/momirealms/customfishing/api/util/SimpleLocation.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.common; +package net.momirealms.customfishing.api.util; import org.bukkit.Location; @@ -51,13 +51,13 @@ public record SimpleLocation(String worldName, int x, int y, int z) { public int hashCode() { int hash = 3; hash = 19 * hash + (worldName != null ? worldName.hashCode() : 0); - hash = 19 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); - hash = 19 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); - hash = 19 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32)); + hash = 19 * hash + Long.hashCode(Double.doubleToLongBits(this.x)); + hash = 19 * hash + Long.hashCode(Double.doubleToLongBits(this.y)); + hash = 19 * hash + Long.hashCode(Double.doubleToLongBits(this.z)); return hash; } - public static SimpleLocation getByBukkitLocation(Location location) { + public static SimpleLocation of(Location location) { return new SimpleLocation(location.getWorld().getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ()); } } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/util/TagUtils.java b/api/src/main/java/net/momirealms/customfishing/api/util/TagUtils.java new file mode 100644 index 00000000..f83ef1f2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/util/TagUtils.java @@ -0,0 +1,22 @@ +package net.momirealms.customfishing.api.util; + +import net.momirealms.customfishing.api.mechanic.item.tag.TagValueType; +import net.momirealms.customfishing.common.util.Pair; + +import java.util.Locale; + +public class TagUtils { + + public static Pair toTypeAndData(String str) { + String[] parts = str.split("\\s+", 2); + if (parts.length == 1) { + return Pair.of(TagValueType.STRING, parts[0]); + } + if (parts.length != 2) { + throw new IllegalArgumentException("Invalid value format: " + str); + } + TagValueType type = TagValueType.valueOf(parts[0].substring(1, parts[0].length() - 1).toUpperCase(Locale.ENGLISH)); + String data = parts[1]; + return Pair.of(type, data); + } +} diff --git a/build.gradle.kts b/build.gradle.kts index d06408b6..ea3eae0e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,80 +1,47 @@ +import java.io.ByteArrayOutputStream + plugins { id("java") - id("application") - id("maven-publish") - id("io.github.goooler.shadow") version "8.1.7" } -allprojects { +val commitID : String = versionBanner() +ext["commitID"] = commitID - version = "2.1.6.6" +subprojects { - apply() apply(plugin = "java") - apply(plugin = "application") - apply(plugin = "io.github.goooler.shadow") - apply(plugin = "org.gradle.maven-publish") - - application { - mainClass.set("") - } + apply(plugin = "java-library") repositories { mavenCentral() - maven("https://maven.aliyun.com/repository/public/") - maven("https://papermc.io/repo/repository/maven-public/") - maven("https://oss.sonatype.org/content/groups/public/") - maven("https://repo.dmulloy2.net/repository/public/") - maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") - maven("https://repo.codemc.org/repository/maven-public/") - maven("https://maven.enginehub.org/repo/") - maven("https://jitpack.io/") - maven("https://mvn.lumine.io/repository/maven-public/") - maven("https://repo.rapture.pw/repository/maven-releases/") - maven("https://nexus.phoenixdevt.fr/repository/maven-public/") - maven("https://r.irepo.space/maven/") - maven("https://repo.auxilor.io/repository/maven-public/") - maven("https://repo.william278.net/releases/") - maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") - maven("https://repo.minebench.de/") - maven("https://repo.xenondevs.xyz/releases/") - maven("https://repo.oraxen.com/releases") - maven("https://nexus.betonquest.org/repository/betonquest/") - maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") + maven("https://jitpack.io/") // sparrow-heart, rtag + maven("https://papermc.io/repo/repository/maven-public/") // paper + maven("https://oss.sonatype.org/content/repositories/snapshots") + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // spigot + } + + tasks.processResources { + filteringCharset = "UTF-8" + + filesMatching(arrayListOf("library-version.properties")) { + expand(rootProject.properties) + } + + filesMatching(arrayListOf("plugin.yml", "*.yml", "*/*.yml")) { + expand( + Pair("git_version", commitID), + Pair("project_version", rootProject.properties["project_version"]), + Pair("config_version", rootProject.properties["config_version"]) + ) + } } } -subprojects { - tasks.processResources { - val props = mapOf("version" to version) - inputs.properties(props) - filteringCharset = "UTF-8" - filesMatching("*plugin.yml") { - expand(props) - } - } - - tasks.withType { - options.encoding = "UTF-8" - options.release.set(17) - } - - tasks.shadowJar { - destinationDirectory.set(file("$rootDir/target")) - archiveClassifier.set("") - archiveFileName.set("CustomFishing-" + project.name + "-" + project.version + ".jar") - } - - if ("api" == project.name) { - publishing { - publications { - create("mavenJava") { - groupId = "net.momirealms" - artifactId = "CustomFishing" - version = rootProject.version.toString() - artifact(tasks.shadowJar) - } - } - } +fun versionBanner(): String { + val os = ByteArrayOutputStream() + project.exec { + commandLine = "git rev-parse --short=8 HEAD".split(" ") + standardOutput = os } + return String(os.toByteArray()).trim() } \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 00000000..4bdcb717 --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1,38 @@ +repositories { + maven("https://jitpack.io/") // rtag +} + +dependencies { + compileOnly("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}") { + exclude(module = "adventure-bom") + exclude(module = "checker-qual") + exclude(module = "annotations") + } + compileOnly("org.incendo:cloud-core:${rootProject.properties["cloud_core_version"]}") + compileOnly("org.incendo:cloud-minecraft-extras:${rootProject.properties["cloud_minecraft_extras_version"]}") + compileOnly("dev.dejvokep:boosted-yaml:${rootProject.properties["boosted_yaml_version"]}") + compileOnly("org.jetbrains:annotations:${rootProject.properties["jetbrains_annotations_version"]}") + compileOnly("org.slf4j:slf4j-api:${rootProject.properties["slf4j_version"]}") + compileOnly("org.apache.logging.log4j:log4j-core:${rootProject.properties["log4j_version"]}") + compileOnly("net.kyori:adventure-text-minimessage:${rootProject.properties["adventure_bundle_version"]}") + compileOnly("net.kyori:adventure-text-serializer-gson:${rootProject.properties["adventure_bundle_version"]}") + compileOnly("com.google.code.gson:gson:${rootProject.properties["gson_version"]}") + compileOnly("com.github.ben-manes.caffeine:caffeine:${rootProject.properties["caffeine_version"]}") + compileOnly("com.saicone.rtag:rtag:${rootProject.properties["rtag_version"]}") + compileOnly("net.objecthunter:exp4j:${rootProject.properties["exp4j_version"]}") + compileOnly("com.google.guava:guava:${rootProject.properties["guava_version"]}") +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customfishing/common/command/AbstractCommandFeature.java b/common/src/main/java/net/momirealms/customfishing/common/command/AbstractCommandFeature.java new file mode 100644 index 00000000..0597df25 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/command/AbstractCommandFeature.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.command; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.momirealms.customfishing.common.sender.SenderFactory; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.context.CommandContext; + +public abstract class AbstractCommandFeature implements CommandFeature { + + protected final CustomFishingCommandManager commandManager; + protected CommandConfig commandConfig; + + public AbstractCommandFeature(CustomFishingCommandManager commandManager) { + this.commandManager = commandManager; + } + + protected abstract SenderFactory getSenderFactory(); + + public abstract Command.Builder assembleCommand(CommandManager manager, Command.Builder builder); + + @Override + @SuppressWarnings("unchecked") + public Command registerCommand(CommandManager manager, Command.Builder builder) { + Command command = (Command) assembleCommand(manager, builder).build(); + manager.command(command); + return command; + } + + @Override + public void registerRelatedFunctions() { + // empty + } + + @Override + public void unregisterRelatedFunctions() { + // empty + } + + @Override + @SuppressWarnings("unchecked") + public void handleFeedback(CommandContext context, TranslatableComponent.Builder key, Component... args) { + if (context.flags().hasFlag("silent")) { + return; + } + commandManager.handleCommandFeedback((C) context.sender(), key, args); + } + + @Override + public void handleFeedback(C sender, TranslatableComponent.Builder key, Component... args) { + commandManager.handleCommandFeedback(sender, key, args); + } + + @Override + public CustomFishingCommandManager getCustomFishingCommandManager() { + return commandManager; + } + + @Override + public CommandConfig getCommandConfig() { + return commandConfig; + } + + public void setCommandConfig(CommandConfig commandConfig) { + this.commandConfig = commandConfig; + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/command/AbstractCommandManager.java b/common/src/main/java/net/momirealms/customfishing/common/command/AbstractCommandManager.java new file mode 100644 index 00000000..32f9d41a --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/command/AbstractCommandManager.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.command; + +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.TranslatableComponent; +import net.momirealms.customfishing.common.locale.CustomFishingCaptionFormatter; +import net.momirealms.customfishing.common.locale.CustomFishingCaptionProvider; +import net.momirealms.customfishing.common.locale.TranslationManager; +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import net.momirealms.customfishing.common.sender.Sender; +import net.momirealms.customfishing.common.util.ArrayUtils; +import net.momirealms.customfishing.common.util.TriConsumer; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.caption.Caption; +import org.incendo.cloud.caption.StandardCaptionKeys; +import org.incendo.cloud.component.CommandComponent; +import org.incendo.cloud.exception.*; +import org.incendo.cloud.exception.handling.ExceptionContext; +import org.incendo.cloud.minecraft.extras.MinecraftExceptionHandler; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; + +public abstract class AbstractCommandManager implements CustomFishingCommandManager { + + protected final HashSet> registeredRootCommandComponents = new HashSet<>(); + protected final HashSet> registeredFeatures = new HashSet<>(); + protected final CommandManager commandManager; + protected final CustomFishingPlugin plugin; + private final CustomFishingCaptionFormatter captionFormatter = new CustomFishingCaptionFormatter(); + private final MinecraftExceptionHandler.Decorator decorator = (formatter, ctx, msg) -> msg; + + private TriConsumer feedbackConsumer; + + public AbstractCommandManager(CustomFishingPlugin plugin, CommandManager commandManager) { + this.commandManager = commandManager; + this.plugin = plugin; + this.inject(); + this.feedbackConsumer = defaultFeedbackConsumer(); + } + + @Override + public void setFeedbackConsumer(@NotNull TriConsumer feedbackConsumer) { + this.feedbackConsumer = feedbackConsumer; + } + + @Override + public TriConsumer defaultFeedbackConsumer() { + return ((sender, node, component) -> { + wrapSender(sender).sendMessage( + component, true + ); + }); + } + + protected abstract Sender wrapSender(C c); + + private void inject() { + getCommandManager().captionRegistry().registerProvider(new CustomFishingCaptionProvider<>()); + injectExceptionHandler(InvalidSyntaxException.class, MinecraftExceptionHandler.createDefaultInvalidSyntaxHandler(), StandardCaptionKeys.EXCEPTION_INVALID_SYNTAX); + injectExceptionHandler(InvalidCommandSenderException.class, MinecraftExceptionHandler.createDefaultInvalidSenderHandler(), StandardCaptionKeys.EXCEPTION_INVALID_SENDER); + injectExceptionHandler(NoPermissionException.class, MinecraftExceptionHandler.createDefaultNoPermissionHandler(), StandardCaptionKeys.EXCEPTION_NO_PERMISSION); + injectExceptionHandler(ArgumentParseException.class, MinecraftExceptionHandler.createDefaultArgumentParsingHandler(), StandardCaptionKeys.EXCEPTION_INVALID_ARGUMENT); + injectExceptionHandler(CommandExecutionException.class, MinecraftExceptionHandler.createDefaultCommandExecutionHandler(), StandardCaptionKeys.EXCEPTION_UNEXPECTED); + } + + private void injectExceptionHandler(Class type, MinecraftExceptionHandler.MessageFactory factory, Caption key) { + getCommandManager().exceptionController().registerHandler(type, ctx -> { + final @Nullable ComponentLike message = factory.message(captionFormatter, (ExceptionContext) ctx); + if (message != null) { + handleCommandFeedback(ctx.context().sender(), key.key(), decorator.decorate(captionFormatter, ctx, message.asComponent()).asComponent()); + } + }); + } + + @Override + public CommandConfig getCommandConfig(YamlDocument document, String featureID) { + Section section = document.getSection(featureID); + if (section == null) return null; + return new CommandConfig.Builder() + .permission(section.getString("permission")) + .usages(section.getStringList("usage")) + .enable(section.getBoolean("enable", false)) + .build(); + } + + @Override + public Collection> buildCommandBuilders(CommandConfig config) { + ArrayList> list = new ArrayList<>(); + for (String usage : config.getUsages()) { + if (!usage.startsWith("/")) continue; + String command = usage.substring(1).trim(); + String[] split = command.split(" "); + Command.Builder builder = new CommandBuilder.BasicCommandBuilder<>(getCommandManager(), split[0]) + .setCommandNode(ArrayUtils.subArray(split, 1)) + .setPermission(config.getPermission()) + .getBuiltCommandBuilder(); + list.add(builder); + } + return list; + } + + @Override + public void registerFeature(CommandFeature feature, CommandConfig config) { + if (!config.isEnable()) throw new RuntimeException("Registering a disabled command feature is not allowed"); + for (Command.Builder builder : buildCommandBuilders(config)) { + Command command = feature.registerCommand(commandManager, builder); + this.registeredRootCommandComponents.add(command.rootComponent()); + } + feature.registerRelatedFunctions(); + this.registeredFeatures.add(feature); + ((AbstractCommandFeature) feature).setCommandConfig(config); + } + + @Override + public void registerDefaultFeatures() { + YamlDocument document = plugin.getConfigManager().loadConfig(commandsFile); + this.getFeatures().values().forEach(feature -> { + CommandConfig config = getCommandConfig(document, feature.getFeatureID()); + if (config.isEnable()) { + registerFeature(feature, config); + } + }); + } + + @Override + public void unregisterFeatures() { + this.registeredRootCommandComponents.forEach(component -> this.commandManager.commandRegistrationHandler().unregisterRootCommand(component)); + this.registeredRootCommandComponents.clear(); + this.registeredFeatures.forEach(CommandFeature::unregisterRelatedFunctions); + this.registeredFeatures.clear(); + } + + @Override + public CommandManager getCommandManager() { + return commandManager; + } + + @Override + public void handleCommandFeedback(C sender, TranslatableComponent.Builder key, Component... args) { + TranslatableComponent component = key.arguments(args).build(); + this.feedbackConsumer.accept(sender, component.key(), TranslationManager.render(component)); + } + + @Override + public void handleCommandFeedback(C sender, String node, Component component) { + this.feedbackConsumer.accept(sender, node, component); + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/command/CommandBuilder.java b/common/src/main/java/net/momirealms/customfishing/common/command/CommandBuilder.java new file mode 100644 index 00000000..2bf569c2 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/command/CommandBuilder.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.command; + +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public interface CommandBuilder { + + CommandBuilder setPermission(String permission); + + CommandBuilder setCommandNode(String... subNodes); + + Command.Builder getBuiltCommandBuilder(); + + class BasicCommandBuilder implements CommandBuilder { + + private Command.Builder commandBuilder; + + public BasicCommandBuilder(CommandManager commandManager, String rootNode) { + this.commandBuilder = commandManager.commandBuilder(rootNode); + } + + @Override + public CommandBuilder setPermission(String permission) { + this.commandBuilder = this.commandBuilder.permission(permission); + return this; + } + + @Override + public CommandBuilder setCommandNode(String... subNodes) { + for (String sub : subNodes) { + this.commandBuilder = this.commandBuilder.literal(sub); + } + return this; + } + + @Override + public Command.Builder getBuiltCommandBuilder() { + return commandBuilder; + } + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/command/CommandConfig.java b/common/src/main/java/net/momirealms/customfishing/common/command/CommandConfig.java new file mode 100644 index 00000000..d07e0307 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/command/CommandConfig.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.command; + +import java.util.ArrayList; +import java.util.List; + +public class CommandConfig { + + private boolean enable = false; + private List usages = new ArrayList<>(); + private String permission = null; + + private CommandConfig() { + } + + public CommandConfig(boolean enable, List usages, String permission) { + this.enable = enable; + this.usages = usages; + this.permission = permission; + } + + public boolean isEnable() { + return enable; + } + + public List getUsages() { + return usages; + } + + public String getPermission() { + return permission; + } + + public static class Builder { + + private final CommandConfig config; + + public Builder() { + this.config = new CommandConfig<>(); + } + + public Builder usages(List usages) { + config.usages = usages; + return this; + } + + public Builder permission(String permission) { + config.permission = permission; + return this; + } + + public Builder enable(boolean enable) { + config.enable = enable; + return this; + } + + public CommandConfig build() { + return config; + } + } +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customfishing/common/command/CommandFeature.java b/common/src/main/java/net/momirealms/customfishing/common/command/CommandFeature.java new file mode 100644 index 00000000..b3f88835 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/command/CommandFeature.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.command; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.context.CommandContext; + +public interface CommandFeature { + + Command registerCommand(CommandManager cloudCommandManager, Command.Builder builder); + + String getFeatureID(); + + void registerRelatedFunctions(); + + void unregisterRelatedFunctions(); + + void handleFeedback(CommandContext context, TranslatableComponent.Builder key, Component... args); + + void handleFeedback(C sender, TranslatableComponent.Builder key, Component... args); + + CustomFishingCommandManager getCustomFishingCommandManager(); + + CommandConfig getCommandConfig(); +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/command/CustomFishingCommandManager.java b/common/src/main/java/net/momirealms/customfishing/common/command/CustomFishingCommandManager.java new file mode 100644 index 00000000..09b69c55 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/command/CustomFishingCommandManager.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.command; + +import dev.dejvokep.boostedyaml.YamlDocument; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.util.Index; +import net.momirealms.customfishing.common.util.TriConsumer; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +public interface CustomFishingCommandManager { + + String commandsFile = "commands.yml"; + + void unregisterFeatures(); + + void registerFeature(CommandFeature feature, CommandConfig config); + + void registerDefaultFeatures(); + + Index> getFeatures(); + + void setFeedbackConsumer(@NotNull TriConsumer feedbackConsumer); + + TriConsumer defaultFeedbackConsumer(); + + CommandConfig getCommandConfig(YamlDocument document, String featureID); + + Collection> buildCommandBuilders(CommandConfig config); + + CommandManager getCommandManager(); + + void handleCommandFeedback(C sender, TranslatableComponent.Builder key, Component... args); + + void handleCommandFeedback(C sender, String node, Component component); +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/config/ConfigLoader.java b/common/src/main/java/net/momirealms/customfishing/common/config/ConfigLoader.java new file mode 100644 index 00000000..1f07cb17 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/config/ConfigLoader.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.config; + +import dev.dejvokep.boostedyaml.YamlDocument; + +import java.io.File; + +public interface ConfigLoader { + + YamlDocument loadConfig(String filePath); + + YamlDocument loadConfig(String filePath, char routeSeparator); + + YamlDocument loadData(File file); + + YamlDocument loadData(File file, char routeSeparator); + + void saveResource(String filePath); +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/season/RealisticSeasonsImpl.java b/common/src/main/java/net/momirealms/customfishing/common/config/node/Node.java similarity index 51% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/season/RealisticSeasonsImpl.java rename to common/src/main/java/net/momirealms/customfishing/common/config/node/Node.java index f0999f1f..7357ee78 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/season/RealisticSeasonsImpl.java +++ b/common/src/main/java/net/momirealms/customfishing/common/config/node/Node.java @@ -15,25 +15,43 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.season; +package net.momirealms.customfishing.common.config.node; -import me.casperge.realisticseasons.api.SeasonsAPI; -import net.momirealms.customfishing.api.integration.SeasonInterface; -import org.bukkit.World; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class RealisticSeasonsImpl implements SeasonInterface { +import java.util.HashMap; + +public class Node { + + private final T value; + private final HashMap> childTree = new HashMap<>(); + + public Node(T value) { + this.value = value; + } + + public Node() { + this(null); + } + + @Nullable + public T nodeValue() { + return value; + } @NotNull - @Override - public String getSeason(World world) { - return switch (SeasonsAPI.getInstance().getSeason(world)) { - case WINTER -> "winter"; - case SPRING -> "spring"; - case SUMMER -> "summer"; - case FALL -> "autumn"; - case DISABLED -> "disabled"; - case RESTORE -> "restore"; - }; + public HashMap> getChildTree() { + return childTree; + } + + @Nullable + public Node getChild(String node) { + return childTree.get(node); + } + + @Nullable + public Node removeChild(String node) { + return childTree.remove(node); } } diff --git a/common/src/main/java/net/momirealms/customfishing/common/dependency/Dependency.java b/common/src/main/java/net/momirealms/customfishing/common/dependency/Dependency.java new file mode 100644 index 00000000..0570aa42 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/dependency/Dependency.java @@ -0,0 +1,357 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.common.dependency; + +import net.momirealms.customfishing.common.dependency.relocation.Relocation; +import org.jetbrains.annotations.Nullable; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +/** + * The dependencies used by CustomFishing. + */ +public enum Dependency { + + ASM( + "org.ow2.asm", + "asm", + "maven", + "asm" + ), + ASM_COMMONS( + "org.ow2.asm", + "asm-commons", + "maven", + "asm-commons" + ), + JAR_RELOCATOR( + "me.lucko", + "jar-relocator", + "maven", + "jar-relocator" + ), + H2_DRIVER( + "com.h2database", + "h2", + "maven", + "h2-driver" + ), + SQLITE_DRIVER( + "org.xerial", + "sqlite-jdbc", + "maven", + "sqlite-driver" + ), + CLOUD_CORE( + "org{}incendo", + "cloud-core", + "maven", + "cloud-core", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_BRIGADIER( + "org{}incendo", + "cloud-brigadier", + "maven", + "cloud-brigadier", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_SERVICES( + "org{}incendo", + "cloud-services", + "maven", + "cloud-services", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_BUKKIT( + "org{}incendo", + "cloud-bukkit", + "maven", + "cloud-bukkit", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_PAPER( + "org{}incendo", + "cloud-paper", + "maven", + "cloud-paper", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_MINECRAFT_EXTRAS( + "org{}incendo", + "cloud-minecraft-extras", + "maven", + "cloud-minecraft-extras", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("adventure", "net{}kyori{}adventure"), + Relocation.of("option", "net{}kyori{}option"), + Relocation.of("examination", "net{}kyori{}examination"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + GEANTY_REF( + "io{}leangen{}geantyref", + "geantyref", + "maven", + "geantyref", + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + BOOSTED_YAML( + "dev{}dejvokep", + "boosted-yaml", + "maven", + "boosted-yaml", + Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml") + ), + BYTEBUDDY( + "net{}bytebuddy", + "byte-buddy", + "maven", + "byte-buddy", + Relocation.of("bytebuddy", "net{}bytebuddy") + ), + MARIADB_DRIVER( + "org{}mariadb{}jdbc", + "mariadb-java-client", + "maven", + "mariadb-java-client", + Relocation.of("mariadb", "org{}mariadb") + ), + MYSQL_DRIVER( + "com{}mysql", + "mysql-connector-j", + "maven", + "mysql-connector-j", + Relocation.of("mysql", "com{}mysql") + ), + HIKARI_CP( + "com{}zaxxer", + "HikariCP", + "maven", + "hikari-cp", + Relocation.of("hikari", "com{}zaxxer{}hikari") + ), + MONGODB_DRIVER_CORE( + "org{}mongodb", + "mongodb-driver-core", + "maven", + "mongodb-driver-core", + Relocation.of("mongodb", "com{}mongodb"), + Relocation.of("bson", "org{}bson") + ), + MONGODB_DRIVER_SYNC( + "org{}mongodb", + "mongodb-driver-sync", + "maven", + "mongodb-driver-sync", + Relocation.of("mongodb", "com{}mongodb"), + Relocation.of("bson", "org{}bson") + ) { + @Override + public String getVersion() { + return Dependency.MONGODB_DRIVER_CORE.getVersion(); + } + }, + MONGODB_DRIVER_BSON( + "org{}mongodb", + "bson", + "maven", + "mongodb-bson", + Relocation.of("mongodb", "com{}mongodb"), + Relocation.of("bson", "org{}bson") + ) { + @Override + public String getVersion() { + return Dependency.MONGODB_DRIVER_CORE.getVersion(); + } + }, + COMMONS_POOL_2( + "org{}apache{}commons", + "commons-pool2", + "maven", + "commons-pool", + Relocation.of("commonspool2", "org{}apache{}commons{}pool2") + ), + BSTATS_BASE( + "org{}bstats", + "bstats-base", + "maven", + "bstats-base", + Relocation.of("bstats", "org{}bstats") + ), + BSTATS_BUKKIT( + "org{}bstats", + "bstats-bukkit", + "maven", + "bstats-bukkit", + Relocation.of("bstats", "org{}bstats") + ) { + @Override + public String getVersion() { + return Dependency.BSTATS_BASE.getVersion(); + } + }, + GSON( + "com.google.code.gson", + "gson", + "maven", + "gson" + ), + CAFFEINE( + "com{}github{}ben-manes{}caffeine", + "caffeine", + "maven", + "caffeine", + Relocation.of("caffeine", "com{}github{}benmanes{}caffeine") + ), + JEDIS( + "redis{}clients", + "jedis", + "maven", + "jedis", + Relocation.of("jedis", "redis{}clients{}jedis"), + Relocation.of("commonspool2", "org{}apache{}commons{}pool2") + ), + EXP4J( + "net{}objecthunter", + "exp4j", + "maven", + "exp4j", + Relocation.of("exp4j", "net{}objecthunter{}exp4j") + ), + SLF4J_SIMPLE( + "org.slf4j", + "slf4j-simple", + "maven", + "slf4j_simple" + ) { + @Override + public String getVersion() { + return Dependency.SLF4J_API.getVersion(); + } + }, + SLF4J_API( + "org.slf4j", + "slf4j-api", + "maven", + "slf4j" + ), + LZ4( + "org{}lz4", + "lz4-java", + "maven", + "lz4-java", + Relocation.of("jpountz", "net{}jpountz") + ); + + private final List relocations; + private final String repo; + private final String groupId; + private String rawArtifactId; + private String customArtifactID; + + private static final String MAVEN_FORMAT = "%s/%s/%s/%s-%s.jar"; + + Dependency(String groupId, String rawArtifactId, String repo, String customArtifactID) { + this(groupId, rawArtifactId, repo, customArtifactID, new Relocation[0]); + } + + Dependency(String groupId, String rawArtifactId, String repo, String customArtifactID, Relocation... relocations) { + this.rawArtifactId = rawArtifactId; + this.groupId = groupId; + this.relocations = new ArrayList<>(Arrays.stream(relocations).toList()); + this.repo = repo; + this.customArtifactID = customArtifactID; + } + + public Dependency setCustomArtifactID(String customArtifactID) { + this.customArtifactID = customArtifactID; + return this; + } + + public Dependency setRawArtifactID(String artifactId) { + this.rawArtifactId = artifactId; + return this; + } + + public String getVersion() { + return DependencyProperties.getDependencyVersion(customArtifactID); + } + + private static String rewriteEscaping(String s) { + return s.replace("{}", "."); + } + + public String getFileName(String classifier) { + String name = customArtifactID.toLowerCase(Locale.ROOT).replace('_', '-'); + String extra = classifier == null || classifier.isEmpty() + ? "" + : "-" + classifier; + return name + "-" + this.getVersion() + extra + ".jar"; + } + + String getMavenRepoPath() { + return String.format(MAVEN_FORMAT, + rewriteEscaping(groupId).replace(".", "/"), + rewriteEscaping(rawArtifactId), + getVersion(), + rewriteEscaping(rawArtifactId), + getVersion() + ); + } + + public List getRelocations() { + return this.relocations; + } + + /** + * Creates a {@link MessageDigest} suitable for computing the checksums + * of dependencies. + * + * @return the digest + */ + public static MessageDigest createDigest() { + try { + return MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + @Nullable + public String getRepo() { + return repo; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyDownloadException.java b/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyDownloadException.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyDownloadException.java rename to common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyDownloadException.java index f48aa426..6bbf30a7 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyDownloadException.java +++ b/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyDownloadException.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.dependencies; +package net.momirealms.customfishing.common.dependency; /** * Exception thrown if a dependency cannot be downloaded. diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyManager.java b/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyManager.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyManager.java rename to common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyManager.java index 2f09a5a0..81f1b229 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyManager.java +++ b/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyManager.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.dependencies; +package net.momirealms.customfishing.common.dependency; import java.util.Collection; import java.util.Set; diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyManagerImpl.java b/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyManagerImpl.java similarity index 68% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyManagerImpl.java rename to common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyManagerImpl.java index 878bb175..7364164a 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyManagerImpl.java +++ b/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyManagerImpl.java @@ -23,19 +23,15 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.dependencies; +package net.momirealms.customfishing.common.dependency; -import com.google.common.collect.ImmutableSet; -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.libraries.classpath.ClassPathAppender; -import net.momirealms.customfishing.libraries.dependencies.classloader.IsolatedClassLoader; -import net.momirealms.customfishing.libraries.dependencies.relocation.Relocation; -import net.momirealms.customfishing.libraries.dependencies.relocation.RelocationHandler; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import net.momirealms.customfishing.common.dependency.classloader.IsolatedClassLoader; +import net.momirealms.customfishing.common.dependency.relocation.Relocation; +import net.momirealms.customfishing.common.dependency.relocation.RelocationHandler; +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import net.momirealms.customfishing.common.plugin.classpath.ClassPathAppender; +import net.momirealms.customfishing.common.util.FileUtils; -import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; @@ -43,6 +39,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; /** * Loads and manages runtime dependencies for the plugin. @@ -58,20 +55,24 @@ public class DependencyManagerImpl implements DependencyManager { /** A map of dependencies which have already been loaded. */ private final EnumMap loaded = new EnumMap<>(Dependency.class); /** A map of isolated classloaders which have been created. */ - private final Map, IsolatedClassLoader> loaders = new HashMap<>(); + private final Map, IsolatedClassLoader> loaders = new HashMap<>(); /** Cached relocation handler instance. */ - private @MonotonicNonNull RelocationHandler relocationHandler = null; + private final RelocationHandler relocationHandler; + private final Executor loadingExecutor; + private final CustomFishingPlugin plugin; - public DependencyManagerImpl(CustomFishingPluginImpl plugin, ClassPathAppender classPathAppender) { + public DependencyManagerImpl(CustomFishingPlugin plugin) { + this.plugin = plugin; this.registry = new DependencyRegistry(); this.cacheDirectory = setupCacheDirectory(plugin); - this.classPathAppender = classPathAppender; + this.classPathAppender = plugin.getClassPathAppender(); + this.loadingExecutor = plugin.getScheduler().async(); this.relocationHandler = new RelocationHandler(this); } @Override public ClassLoader obtainClassLoaderWith(Set dependencies) { - ImmutableSet set = ImmutableSet.copyOf(dependencies); + Set set = new HashSet<>(dependencies); for (Dependency dependency : dependencies) { if (!this.loaded.containsKey(dependency)) { @@ -112,13 +113,15 @@ public class DependencyManagerImpl implements DependencyManager { continue; } - try { - loadDependency(dependency); - } catch (Throwable e) { - new RuntimeException("Unable to load dependency " + dependency.name(), e).printStackTrace(); - } finally { - latch.countDown(); - } + this.loadingExecutor.execute(() -> { + try { + loadDependency(dependency); + } catch (Throwable e) { + this.plugin.getPluginLogger().warn("Unable to load dependency " + dependency.name(), e); + } finally { + latch.countDown(); + } + }); } try { @@ -143,7 +146,8 @@ public class DependencyManagerImpl implements DependencyManager { } private Path downloadDependency(Dependency dependency) throws DependencyDownloadException { - Path file = this.cacheDirectory.resolve(dependency.getFileName(null)); + String fileName = dependency.getFileName(null); + Path file = this.cacheDirectory.resolve(fileName); // if the file already exists, don't attempt to re-download it. if (Files.exists(file)) { @@ -151,18 +155,20 @@ public class DependencyManagerImpl implements DependencyManager { } DependencyDownloadException lastError = null; - - String fileName = dependency.getFileName(null); - - // attempt to download the dependency from each repo in order. - for (DependencyRepository repo : DependencyRepository.values()) { - try { - LogUtils.info("Downloading dependency(" + fileName + ") from " + repo.getUrl() + dependency.getMavenRepoPath()); - repo.download(dependency, file); - LogUtils.info("Successfully downloaded " + fileName); - return file; - } catch (DependencyDownloadException e) { - lastError = e; + String forceRepo = dependency.getRepo(); + List repository = DependencyRepository.getByID(forceRepo); + if (!repository.isEmpty()) { + int i = 0; + while (i < repository.size()) { + try { + plugin.getPluginLogger().info("Downloading dependency(" + fileName + ") from " + repository.get(i).getUrl() + dependency.getMavenRepoPath()); + repository.get(i).download(dependency, file); + plugin.getPluginLogger().info("Successfully downloaded " + fileName); + return file; + } catch (DependencyDownloadException e) { + lastError = e; + i++; + } } } throw Objects.requireNonNull(lastError); @@ -181,16 +187,21 @@ public class DependencyManagerImpl implements DependencyManager { return remappedFile; } - LogUtils.info("Remapping " + dependency.getFileName(null)); + plugin.getPluginLogger().info("Remapping " + dependency.getFileName(null)); relocationHandler.remap(normalFile, remappedFile, rules); - LogUtils.info("Successfully remapped " + dependency.getFileName(null)); + plugin.getPluginLogger().info("Successfully remapped " + dependency.getFileName(null)); return remappedFile; } private static Path setupCacheDirectory(CustomFishingPlugin plugin) { - File folder = new File(plugin.getDataFolder(), "libs"); - folder.mkdirs(); - return folder.toPath(); + Path cacheDirectory = plugin.getDataDirectory().resolve("libs"); + try { + FileUtils.createDirectoriesIfNotExists(cacheDirectory); + } catch (IOException e) { + throw new RuntimeException("Unable to create libs directory", e); + } + + return cacheDirectory; } @Override @@ -210,8 +221,7 @@ public class DependencyManagerImpl implements DependencyManager { } if (firstEx != null) { - firstEx.printStackTrace(); + plugin.getPluginLogger().severe(firstEx.getMessage(), firstEx); } } - } diff --git a/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyProperties.java b/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyProperties.java new file mode 100644 index 00000000..132781bd --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyProperties.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.dependency; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class DependencyProperties { + + private final HashMap versionMap; + + private DependencyProperties(HashMap versionMap) { + this.versionMap = versionMap; + } + + public static String getDependencyVersion(String dependencyID) { + if (!SingletonHolder.INSTANCE.versionMap.containsKey(dependencyID)) { + throw new RuntimeException("Unknown dependency: " + dependencyID); + } + return SingletonHolder.INSTANCE.versionMap.get(dependencyID); + } + + private static class SingletonHolder { + + private static final DependencyProperties INSTANCE = getInstance(); + + private static DependencyProperties getInstance() { + try (InputStream inputStream = DependencyProperties.class.getClassLoader().getResourceAsStream("library-version.properties")) { + HashMap versionMap = new HashMap<>(); + Properties properties = new Properties(); + properties.load(inputStream); + for (Map.Entry entry : properties.entrySet()) { + if (entry.getKey() instanceof String key && entry.getValue() instanceof String value) { + versionMap.put(key, value); + } + } + return new DependencyProperties(versionMap); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyRegistry.java b/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyRegistry.java similarity index 97% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyRegistry.java rename to common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyRegistry.java index 0218d0c0..cc58a661 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyRegistry.java +++ b/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyRegistry.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.dependencies; +package net.momirealms.customfishing.common.dependency; import com.google.gson.JsonElement; diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyRepository.java b/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyRepository.java similarity index 80% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyRepository.java rename to common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyRepository.java index a8538048..ac51e65e 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/DependencyRepository.java +++ b/common/src/main/java/net/momirealms/customfishing/common/dependency/DependencyRepository.java @@ -23,9 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.dependencies; - -import com.google.common.io.ByteStreams; +package net.momirealms.customfishing.common.dependency; import java.io.IOException; import java.io.InputStream; @@ -33,38 +31,53 @@ import java.net.URL; import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.TimeUnit; +import java.util.ArrayList; +import java.util.List; /** * Represents a repository which contains {@link Dependency}s. */ public enum DependencyRepository { - MAVEN_CENTRAL("https://repo1.maven.org/maven2/") { - @Override - protected URLConnection openConnection(Dependency dependency) throws IOException { - URLConnection connection = super.openConnection(dependency); - connection.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(5)); - connection.setReadTimeout((int) TimeUnit.SECONDS.toMillis(5)); - return connection; - } - }, - /** * Maven Central */ - MAVEN_CENTRAL_MIRROR("https://maven.aliyun.com/repository/public/"); + MAVEN_CENTRAL("maven", "https://repo1.maven.org/maven2/") { + @Override + protected URLConnection openConnection(Dependency dependency) throws IOException { + URLConnection connection = super.openConnection(dependency); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + return connection; + } + }, + /** + * Maven Central Mirror + */ + MAVEN_CENTRAL_MIRROR("maven", "https://maven.aliyun.com/repository/public/"); private final String url; + private final String id; - DependencyRepository(String url) { + DependencyRepository(String id, String url) { this.url = url; + this.id = id; } public String getUrl() { return url; } + public static List getByID(String id) { + ArrayList repositories = new ArrayList<>(); + for (DependencyRepository repository : values()) { + if (id.equals(repository.id)) { + repositories.add(repository); + } + } + return repositories; + } + /** * Opens a connection to the given {@code dependency}. * @@ -88,7 +101,7 @@ public enum DependencyRepository { try { URLConnection connection = openConnection(dependency); try (InputStream in = connection.getInputStream()) { - byte[] bytes = ByteStreams.toByteArray(in); + byte[] bytes = in.readAllBytes(); if (bytes.length == 0) { throw new DependencyDownloadException("Empty stream"); } @@ -123,4 +136,8 @@ public enum DependencyRepository { throw new DependencyDownloadException(e); } } + + public String getId() { + return id; + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/classloader/IsolatedClassLoader.java b/common/src/main/java/net/momirealms/customfishing/common/dependency/classloader/IsolatedClassLoader.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/classloader/IsolatedClassLoader.java rename to common/src/main/java/net/momirealms/customfishing/common/dependency/classloader/IsolatedClassLoader.java index 0dbe284c..24be0818 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/classloader/IsolatedClassLoader.java +++ b/common/src/main/java/net/momirealms/customfishing/common/dependency/classloader/IsolatedClassLoader.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.dependencies.classloader; +package net.momirealms.customfishing.common.dependency.classloader; import java.net.URL; import java.net.URLClassLoader; diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/relocation/Relocation.java b/common/src/main/java/net/momirealms/customfishing/common/dependency/relocation/Relocation.java similarity index 97% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/relocation/Relocation.java rename to common/src/main/java/net/momirealms/customfishing/common/dependency/relocation/Relocation.java index e4513f49..c7a6c8dc 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/relocation/Relocation.java +++ b/common/src/main/java/net/momirealms/customfishing/common/dependency/relocation/Relocation.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.dependencies.relocation; +package net.momirealms.customfishing.common.dependency.relocation; import java.util.Objects; diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/relocation/RelocationHandler.java b/common/src/main/java/net/momirealms/customfishing/common/dependency/relocation/RelocationHandler.java similarity index 89% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/relocation/RelocationHandler.java rename to common/src/main/java/net/momirealms/customfishing/common/dependency/relocation/RelocationHandler.java index 8479442c..90a257be 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/relocation/RelocationHandler.java +++ b/common/src/main/java/net/momirealms/customfishing/common/dependency/relocation/RelocationHandler.java @@ -23,12 +23,11 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.dependencies.relocation; +package net.momirealms.customfishing.common.dependency.relocation; - -import net.momirealms.customfishing.libraries.dependencies.Dependency; -import net.momirealms.customfishing.libraries.dependencies.DependencyManager; -import net.momirealms.customfishing.libraries.dependencies.classloader.IsolatedClassLoader; +import net.momirealms.customfishing.common.dependency.Dependency; +import net.momirealms.customfishing.common.dependency.DependencyManager; +import net.momirealms.customfishing.common.dependency.classloader.IsolatedClassLoader; import java.io.File; import java.io.IOException; @@ -67,8 +66,8 @@ public class RelocationHandler { this.jarRelocatorRunMethod.setAccessible(true); } catch (Exception e) { try { - if (classLoader instanceof IsolatedClassLoader) { - ((IsolatedClassLoader) classLoader).close(); + if (classLoader instanceof IsolatedClassLoader isolatedClassLoader) { + isolatedClassLoader.close(); } } catch (IOException ex) { e.addSuppressed(ex); diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/relocation/RelocationHelper.java b/common/src/main/java/net/momirealms/customfishing/common/dependency/relocation/RelocationHelper.java similarity index 95% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/relocation/RelocationHelper.java rename to common/src/main/java/net/momirealms/customfishing/common/dependency/relocation/RelocationHelper.java index 76958563..84207a41 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/relocation/RelocationHelper.java +++ b/common/src/main/java/net/momirealms/customfishing/common/dependency/relocation/RelocationHelper.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.dependencies.relocation; +package net.momirealms.customfishing.common.dependency.relocation; public final class RelocationHelper { @@ -32,6 +32,5 @@ public final class RelocationHelper { public static final String OKHTTP3_STRING = String.valueOf(new char[]{'o', 'k', 'h', 't', 't', 'p', '3'}); private RelocationHelper() { - } } diff --git a/common/src/main/java/net/momirealms/customfishing/common/helper/AdventureHelper.java b/common/src/main/java/net/momirealms/customfishing/common/helper/AdventureHelper.java new file mode 100644 index 00000000..319d6e70 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/helper/AdventureHelper.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.helper; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.title.Title; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +public class AdventureHelper { + + private final MiniMessage miniMessage; + private final MiniMessage miniMessageStrict; + private final GsonComponentSerializer gsonComponentSerializer; + private final Cache miniMessageToJsonCache = Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build(); + public static boolean legacySupport = false; + + private AdventureHelper() { + this.miniMessage = MiniMessage.builder().build(); + this.miniMessageStrict = MiniMessage.builder().strict(true).build(); + this.gsonComponentSerializer = GsonComponentSerializer.builder().build(); + } + + private static class SingletonHolder { + private static final AdventureHelper INSTANCE = new AdventureHelper(); + } + + public static AdventureHelper getInstance() { + return SingletonHolder.INSTANCE; + } + + public static Component miniMessage(String text) { + if (legacySupport) { + return getMiniMessage().deserialize(legacyToMiniMessage(text)); + } else { + return getMiniMessage().deserialize(text); + } + } + + public static MiniMessage getMiniMessage() { + return getInstance().miniMessage; + } + + public static GsonComponentSerializer getGson() { + return getInstance().gsonComponentSerializer; + } + + public static String miniMessageToJson(String miniMessage) { + AdventureHelper instance = getInstance(); + return instance.miniMessageToJsonCache.get(miniMessage, (text) -> instance.gsonComponentSerializer.serialize(miniMessage(text))); + } + + public static void sendTitle(Audience audience, Component title, Component subtitle, int fadeIn, int stay, int fadeOut) { + audience.showTitle(Title.title(title, subtitle, Title.Times.times(Duration.ofMillis(fadeIn * 50L), Duration.ofMillis(stay * 50L), Duration.ofMillis(fadeOut * 50L)))); + } + + public static void sendActionBar(Audience audience, Component actionBar) { + audience.sendActionBar(actionBar); + } + + public static void sendMessage(Audience audience, Component message) { + audience.sendMessage(message); + } + + public static void playSound(Audience audience, Sound sound) { + audience.playSound(sound); + } + + public static String surroundWithMiniMessageFont(String text, Key font) { + return "" + text + ""; + } + + public static String surroundWithMiniMessageFont(String text, String font) { + return "" + text + ""; + } + + public static String jsonToMiniMessage(String json) { + return getInstance().miniMessageStrict.serialize(getInstance().gsonComponentSerializer.deserialize(json)); + } + + public static Component jsonToComponent(String json) { + return getInstance().gsonComponentSerializer.deserialize(json); + } + + public static String legacyToMiniMessage(String legacy) { + StringBuilder stringBuilder = new StringBuilder(); + char[] chars = legacy.toCharArray(); + for (int i = 0; i < chars.length; i++) { + if (!isColorCode(chars[i])) { + stringBuilder.append(chars[i]); + continue; + } + if (i + 1 >= chars.length) { + stringBuilder.append(chars[i]); + continue; + } + switch (chars[i+1]) { + case '0' -> stringBuilder.append(""); + case '1' -> stringBuilder.append(""); + case '2' -> stringBuilder.append(""); + case '3' -> stringBuilder.append(""); + case '4' -> stringBuilder.append(""); + case '5' -> stringBuilder.append(""); + case '6' -> stringBuilder.append(""); + case '7' -> stringBuilder.append(""); + case '8' -> stringBuilder.append(""); + case '9' -> stringBuilder.append(""); + case 'a' -> stringBuilder.append(""); + case 'b' -> stringBuilder.append(""); + case 'c' -> stringBuilder.append(""); + case 'd' -> stringBuilder.append(""); + case 'e' -> stringBuilder.append(""); + case 'f' -> stringBuilder.append(""); + case 'r' -> stringBuilder.append(""); + case 'l' -> stringBuilder.append(""); + case 'm' -> stringBuilder.append(""); + case 'o' -> stringBuilder.append(""); + case 'n' -> stringBuilder.append(""); + case 'k' -> stringBuilder.append(""); + case 'x' -> { + if (i + 13 >= chars.length + || !isColorCode(chars[i+2]) + || !isColorCode(chars[i+4]) + || !isColorCode(chars[i+6]) + || !isColorCode(chars[i+8]) + || !isColorCode(chars[i+10]) + || !isColorCode(chars[i+12])) { + stringBuilder.append(chars[i]); + continue; + } + stringBuilder + .append("<#") + .append(chars[i+3]) + .append(chars[i+5]) + .append(chars[i+7]) + .append(chars[i+9]) + .append(chars[i+11]) + .append(chars[i+13]) + .append(">"); + i += 12; + } + default -> { + stringBuilder.append(chars[i]); + continue; + } + } + i++; + } + return stringBuilder.toString(); + } + + public static String componentToJson(Component component) { + return getGson().serialize(component); + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public static boolean isColorCode(char c) { + return c == '§' || c == '&'; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/ParentPage.java b/common/src/main/java/net/momirealms/customfishing/common/helper/ExpressionHelper.java similarity index 72% rename from plugin/src/main/java/net/momirealms/customfishing/gui/ParentPage.java rename to common/src/main/java/net/momirealms/customfishing/common/helper/ExpressionHelper.java index 55ac592d..f418c795 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/ParentPage.java +++ b/common/src/main/java/net/momirealms/customfishing/common/helper/ExpressionHelper.java @@ -15,16 +15,13 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.gui; +package net.momirealms.customfishing.common.helper; -import net.momirealms.customfishing.gui.icon.BackToPageItem; -import xyz.xenondevs.invui.item.Item; +import net.objecthunter.exp4j.ExpressionBuilder; -public interface ParentPage { +public class ExpressionHelper { - void reOpen(); - - default Item getBackItem() { - return new BackToPageItem(this); + public static double evaluate(String expression) { + return new ExpressionBuilder(expression).build().evaluate(); } } diff --git a/common/src/main/java/net/momirealms/customfishing/common/helper/GsonHelper.java b/common/src/main/java/net/momirealms/customfishing/common/helper/GsonHelper.java new file mode 100644 index 00000000..fb9ba96a --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/helper/GsonHelper.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.helper; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class GsonHelper { + + private final Gson gson; + + public GsonHelper() { + this.gson = new GsonBuilder() + .create(); + } + + public Gson getGson() { + return gson; + } + + public static Gson get() { + return SingletonHolder.INSTANCE.getGson(); + } + + private static class SingletonHolder { + private static final GsonHelper INSTANCE = new GsonHelper(); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/version/VersionManagerImpl.java b/common/src/main/java/net/momirealms/customfishing/common/helper/VersionHelper.java similarity index 67% rename from plugin/src/main/java/net/momirealms/customfishing/version/VersionManagerImpl.java rename to common/src/main/java/net/momirealms/customfishing/common/helper/VersionHelper.java index 99a687ff..54e37652 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/version/VersionManagerImpl.java +++ b/common/src/main/java/net/momirealms/customfishing/common/helper/VersionHelper.java @@ -15,12 +15,9 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.version; +package net.momirealms.customfishing.common.helper; -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.api.manager.VersionManager; -import net.momirealms.customfishing.api.util.LogUtils; -import org.bukkit.Bukkit; +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; import java.io.BufferedReader; import java.io.InputStream; @@ -28,93 +25,17 @@ import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; /** * This class implements the VersionManager interface and is responsible for managing version-related information. */ -public class VersionManagerImpl implements VersionManager { - - private final CustomFishingPluginImpl plugin; - private final float mcVersion; - private boolean isFolia; - private boolean isMojmap; - private boolean isSpigot; - private final String pluginVersion; - - @SuppressWarnings("deprecation") - public VersionManagerImpl(CustomFishingPluginImpl plugin) { - this.plugin = plugin; - // Get the server version - - String[] split = Bukkit.getServer().getBukkitVersion().split("-")[0].split("\\."); - this.mcVersion = Float.parseFloat(split[1] + "." + (split.length >= 3 ? split[2] : "0")); - - // Get the plugin version - this.pluginVersion = plugin.getDescription().getVersion(); - - this.isSpigot = Bukkit.getServer().getName().equals("CraftBukkit"); - - // Check if the server is Folia - try { - Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); - this.isFolia = true; - } catch (ClassNotFoundException ignored) { - this.isFolia = false; - } - - // Check if the server is Mojmap - try { - Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket"); - this.isMojmap = true; - } catch (ClassNotFoundException ignored) { - } - } - - @Override - public boolean isVersionNewerThan1_19() { - return mcVersion >= 19; - } - - @Override - public boolean isVersionNewerThan1_19_R3() { - return mcVersion >= 19.4; - } - - @Override - public boolean isVersionNewerThan1_19_R2() { - return mcVersion >= 19.3; - } - - @Override - public boolean isVersionNewerThan1_20() { - return mcVersion >= 20; - } - - @Override - public boolean isSpigot() { - return false; - } - - @Override - public String getPluginVersion() { - return pluginVersion; - } - - @Override - public boolean hasRegionScheduler() { - return isFolia; - } - - @Override - public boolean isMojmap() { - return isMojmap; - } +public class VersionHelper { // Method to asynchronously check for plugin updates - @Override - public CompletableFuture checkUpdate() { + public static final Function> UPDATE_CHECKER = (plugin) -> { CompletableFuture updateFuture = new CompletableFuture<>(); - plugin.getScheduler().runTaskAsync(() -> { + plugin.getScheduler().async().execute(() -> { try { URL url = new URL("https://api.polymart.org/v1/getResourceInfoSimple/?resource_id=2723&key=version"); URLConnection conn = url.openConnection(); @@ -122,7 +43,7 @@ public class VersionManagerImpl implements VersionManager { conn.setReadTimeout(60000); InputStream inputStream = conn.getInputStream(); String newest = new BufferedReader(new InputStreamReader(inputStream)).readLine(); - String current = plugin.getVersionManager().getPluginVersion(); + String current = plugin.getPluginVersion(); inputStream.close(); if (!compareVer(newest, current)) { updateFuture.complete(false); @@ -130,15 +51,75 @@ public class VersionManagerImpl implements VersionManager { } updateFuture.complete(true); } catch (Exception exception) { - LogUtils.warn("Error occurred when checking update.", exception); + plugin.getPluginLogger().warn("Error occurred when checking update.", exception); updateFuture.complete(false); } }); return updateFuture; + }; + + private static float version; + private static boolean mojmap; + private static boolean folia; + + public static void init(String serverVersion) { + String[] split = serverVersion.split("\\."); + version = Float.parseFloat(split[1] + "." + (split.length == 3 ? split[2] : "0")); + checkMojMap(); + checkFolia(); + } + + private static void checkMojMap() { + // Check if the server is Mojmap + try { + Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket"); + mojmap = true; + } catch (ClassNotFoundException ignored) { + } + } + + private static void checkFolia() { + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + folia = true; + } catch (ClassNotFoundException ignored) { + } + } + + public static boolean isVersionNewerThan1_19() { + return version >= 19; + } + + public static boolean isVersionNewerThan1_19_4() { + return version >= 19.4; + } + + public static boolean isVersionNewerThan1_19_3() { + return version >= 19.3; + } + + public static boolean isVersionNewerThan1_20() { + return version >= 20.0; + } + + public static boolean isVersionNewerThan1_20_5() { + return version >= 20.5; + } + + public boolean isNewerThan1_20_5() { + return version >= 20.5; + } + + public static boolean isFolia() { + return folia; + } + + public static boolean isMojmap() { + return mojmap; } // Method to compare two version strings - private boolean compareVer(String newV, String currentV) { + private static boolean compareVer(String newV, String currentV) { if (newV == null || currentV == null || newV.isEmpty() || currentV.isEmpty()) { return false; } diff --git a/common/src/main/java/net/momirealms/customfishing/common/item/AbstractItem.java b/common/src/main/java/net/momirealms/customfishing/common/item/AbstractItem.java new file mode 100644 index 00000000..0db7e297 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/item/AbstractItem.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.item; + +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import net.momirealms.customfishing.common.util.Key; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class AbstractItem implements Item { + + private final CustomFishingPlugin plugin; + private final ItemFactory factory; + private final R item; + + AbstractItem(CustomFishingPlugin plugin, ItemFactory factory, R item) { + this.plugin = plugin; + this.factory = factory; + this.item = item; + } + + @Override + public Item damage(Integer data) { + factory.damage(item, data); + return this; + } + + @Override + public Optional damage() { + return factory.damage(item); + } + + @Override + public Item maxDamage(Integer data) { + factory.maxDamage(item, data); + return this; + } + + @Override + public Optional maxDamage() { + return factory.maxDamage(item); + } + + @Override + public Item customModelData(Integer data) { + factory.customModelData(item, data); + return this; + } + + @Override + public Optional customModelData() { + return factory.customModelData(item); + } + + @Override + public Optional displayName() { + return factory.displayName(item); + } + + @Override + public Item lore(List lore) { + factory.lore(item, lore); + return this; + } + + @Override + public Optional> lore() { + return factory.lore(item); + } + + @Override + public Item unbreakable(boolean unbreakable) { + factory.unbreakable(item, unbreakable); + return this; + } + + @Override + public boolean unbreakable() { + return factory.unbreakable(item); + } + + @Override + public Item displayName(String displayName) { + factory.displayName(item, displayName); + return this; + } + + @Override + public Item skull(String data) { + factory.skull(item, data); + return this; + } + + @Override + public Item enchantments(Map enchantments) { + factory.enchantments(item, enchantments); + return this; + } + + @Override + public Item addEnchantment(Key enchantment, int level) { + factory.addEnchantment(item, enchantment, level); + return this; + } + + @Override + public Item storedEnchantments(Map enchantments) { + factory.storedEnchantments(item, enchantments); + return this; + } + + @Override + public Item addStoredEnchantment(Key enchantment, int level) { + factory.addStoredEnchantment(item, enchantment, level); + return this; + } + + @Override + public Item itemFlags(List flags) { + factory.itemFlags(item, flags); + return this; + } + + @Override + public Optional getTag(Object... path) { + return factory.getTag(item, path); + } + + @Override + public Item setTag(Object value, Object... path) { + factory.setTag(item, value, path); + return this; + } + + @Override + public boolean hasTag(Object... path) { + return factory.hasTag(item, path); + } + + @Override + public boolean removeTag(Object... path) { + return factory.removeTag(item, path); + } + + @Override + public I getItem() { + return factory.getItem(item); + } + + @Override + public I load() { + return factory.load(item); + } + + @Override + public I loadCopy() { + return factory.loadCopy(item); + } + + @Override + public void update() { + factory.update(item); + } + + public R getRTagItem() { + return item; + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/item/ComponentKeys.java b/common/src/main/java/net/momirealms/customfishing/common/item/ComponentKeys.java new file mode 100644 index 00000000..c5aa2a71 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/item/ComponentKeys.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.item; + +import net.kyori.adventure.key.Key; + +public class ComponentKeys { + + public static final String CUSTOM_MODEL_DATA = Key.key("minecraft", "custom_model_data").asString(); + public static final String CUSTOM_NAME = Key.key("minecraft", "custom_name").asString(); + public static final String LORE = Key.key("minecraft", "lore").asString(); + public static final String DAMAGE = Key.key("minecraft", "damage").asString(); + public static final String MAX_DAMAGE = Key.key("minecraft", "max_damage").asString(); + public static final String ENCHANTMENT_GLINT_OVERRIDE = Key.key("minecraft", "enchantment_glint_override").asString(); + public static final String ENCHANTMENTS = Key.key("minecraft", "enchantments").asString(); + public static final String STORED_ENCHANTMENTS = Key.key("minecraft", "stored_enchantments").asString(); +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/item/Item.java b/common/src/main/java/net/momirealms/customfishing/common/item/Item.java new file mode 100644 index 00000000..99a6d1a5 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/item/Item.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.item; + +import net.momirealms.customfishing.common.util.Key; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public interface Item { + + Item customModelData(Integer data); + + Optional customModelData(); + + Item damage(Integer data); + + Optional damage(); + + Item maxDamage(Integer data); + + Optional maxDamage(); + + Item displayName(String displayName); + + Optional displayName(); + + Item lore(List lore); + + Optional> lore(); + + Item unbreakable(boolean unbreakable); + + boolean unbreakable(); + + Item skull(String data); + + Item enchantments(Map enchantments); + + Item addEnchantment(Key enchantment, int level); + + Item storedEnchantments(Map enchantments); + + Item addStoredEnchantment(Key enchantment, int level); + + Item itemFlags(List flags); + + Optional getTag(Object... path); + + Item setTag(Object value, Object... path); + + boolean hasTag(Object... path); + + boolean removeTag(Object... path); + + I getItem(); + + I load(); + + I loadCopy(); + + void update(); +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/item/ItemFactory.java b/common/src/main/java/net/momirealms/customfishing/common/item/ItemFactory.java new file mode 100644 index 00000000..9f2e3cd9 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/item/ItemFactory.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.item; + +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import net.momirealms.customfishing.common.util.Key; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public abstract class ItemFactory

{ + + protected final P plugin; + + protected ItemFactory(P plugin) { + this.plugin = plugin; + } + + public Item wrap(R item) { + Objects.requireNonNull(item, "item"); + return new AbstractItem<>(this.plugin, this, item); + } + + protected abstract Optional getTag(R item, Object... path); + + protected abstract void setTag(R item, Object value, Object... path); + + protected abstract boolean hasTag(R item, Object... path); + + protected abstract boolean removeTag(R item, Object... path); + + protected abstract void update(R item); + + protected abstract I load(R item); + + protected abstract I getItem(R item); + + protected abstract I loadCopy(R item); + + protected abstract void customModelData(R item, Integer data); + + protected abstract Optional customModelData(R item); + + protected abstract void displayName(R item, String json); + + protected abstract Optional displayName(R item); + + protected abstract void skull(R item, String skullData); + + protected abstract Optional> lore(R item); + + protected abstract void lore(R item, List lore); + + protected abstract boolean unbreakable(R item); + + protected abstract void unbreakable(R item, boolean unbreakable); + + protected abstract Optional glint(R item); + + protected abstract void glint(R item, Boolean glint); + + protected abstract Optional damage(R item); + + protected abstract void damage(R item, Integer damage); + + protected abstract Optional maxDamage(R item); + + protected abstract void maxDamage(R item, Integer damage); + + protected abstract void enchantments(R item, Map enchantments); + + protected abstract void storedEnchantments(R item, Map enchantments); + + protected abstract void addEnchantment(R item, Key enchantment, int level); + + protected abstract void addStoredEnchantment(R item, Key enchantment, int level); + + protected abstract void itemFlags(R item, List flags); +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/locale/CustomFishingCaptionFormatter.java b/common/src/main/java/net/momirealms/customfishing/common/locale/CustomFishingCaptionFormatter.java new file mode 100644 index 00000000..b63e2e48 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/locale/CustomFishingCaptionFormatter.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.locale; + +import net.kyori.adventure.text.Component; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.caption.Caption; +import org.incendo.cloud.caption.CaptionVariable; +import org.incendo.cloud.minecraft.extras.caption.ComponentCaptionFormatter; + +import java.util.List; + +public class CustomFishingCaptionFormatter implements ComponentCaptionFormatter { + + @Override + public @NonNull Component formatCaption(@NonNull Caption captionKey, @NonNull C recipient, @NonNull String caption, @NonNull List<@NonNull CaptionVariable> variables) { + Component component = ComponentCaptionFormatter.translatable().formatCaption(captionKey, recipient, caption, variables); + return TranslationManager.render(component); + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/locale/CustomFishingCaptionKeys.java b/common/src/main/java/net/momirealms/customfishing/common/locale/CustomFishingCaptionKeys.java new file mode 100644 index 00000000..47dcf374 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/locale/CustomFishingCaptionKeys.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.locale; + +import org.incendo.cloud.caption.Caption; + +public final class CustomFishingCaptionKeys { + + public static final Caption ARGUMENT_PARSE_FAILURE_TIME = Caption.of("argument.parse.failure.time"); + public static final Caption ARGUMENT_PARSE_FAILURE_URL = Caption.of("argument.parse.failure.url"); + public static final Caption ARGUMENT_PARSE_FAILURE_NAMEDTEXTCOLOR = Caption.of("argument.parse.failure.namedtextcolor"); +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/locale/CustomFishingCaptionProvider.java b/common/src/main/java/net/momirealms/customfishing/common/locale/CustomFishingCaptionProvider.java new file mode 100644 index 00000000..226be8bf --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/locale/CustomFishingCaptionProvider.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.locale; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.caption.CaptionProvider; +import org.incendo.cloud.caption.DelegatingCaptionProvider; + +public final class CustomFishingCaptionProvider extends DelegatingCaptionProvider { + + private static final CaptionProvider PROVIDER = CaptionProvider.constantProvider() + .putCaption(CustomFishingCaptionKeys.ARGUMENT_PARSE_FAILURE_URL, "") + .putCaption(CustomFishingCaptionKeys.ARGUMENT_PARSE_FAILURE_TIME, "") + .putCaption(CustomFishingCaptionKeys.ARGUMENT_PARSE_FAILURE_NAMEDTEXTCOLOR, "") + .build(); + + @SuppressWarnings("unchecked") + @Override + public @NonNull CaptionProvider delegate() { + return (CaptionProvider) PROVIDER; + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/locale/MessageConstants.java b/common/src/main/java/net/momirealms/customfishing/common/locale/MessageConstants.java new file mode 100644 index 00000000..58a416b3 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/locale/MessageConstants.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.locale; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; + +public interface MessageConstants { + + TranslatableComponent.Builder COMPETITION_NO_PLAYER = Component.translatable().key("competition.no_player");; + TranslatableComponent.Builder COMPETITION_NO_SCORE = Component.translatable().key("competition.no_score"); + TranslatableComponent.Builder COMPETITION_NO_RANK = Component.translatable().key("competition.no_rank"); + + TranslatableComponent.Builder GOAL_TOTAL_SIZE = Component.translatable().key("competition.goal.total_size"); + TranslatableComponent.Builder GOAL_CATCH_AMOUNT = Component.translatable().key("competition.goal.catch_amount"); + TranslatableComponent.Builder GOAL_TOTAL_SCORE = Component.translatable().key("competition.goal.total_score"); + TranslatableComponent.Builder GOAL_MAX_SIZE = Component.translatable().key("competition.goal.max_size"); + TranslatableComponent.Builder GOAL_MIN_SIZE = Component.translatable().key("competition.goal.min_size"); + + TranslatableComponent.Builder FORMAT_SECOND = Component.translatable().key("format.second"); + TranslatableComponent.Builder FORMAT_MINUTE = Component.translatable().key("format.minute"); + TranslatableComponent.Builder FORMAT_HOUR = Component.translatable().key("format.hour"); + TranslatableComponent.Builder FORMAT_DAY = Component.translatable().key("format.day"); + + TranslatableComponent.Builder COMMAND_RELOAD_SUCCESS = Component.translatable().key("command.reload.success"); + TranslatableComponent.Builder COMMAND_ITEM_FAILURE_NOT_EXIST = Component.translatable().key("command.item.failure.not_exist"); + TranslatableComponent.Builder COMMAND_ITEM_GIVE_SUCCESS = Component.translatable().key("command.item.give.success"); + TranslatableComponent.Builder COMMAND_ITEM_GET_SUCCESS = Component.translatable().key("command.item.get.success"); + TranslatableComponent.Builder COMMAND_ITEM_IMPORT_FAILURE_NO_ITEM = Component.translatable().key("command.item.import.failure.no_item"); + TranslatableComponent.Builder COMMAND_ITEM_IMPORT_SUCCESS = Component.translatable().key("command.item.import.success"); + TranslatableComponent.Builder COMMAND_FISH_FINDER_POSSIBLE_LOOTS = Component.translatable().key("command.fish_finder.possible_loots"); + TranslatableComponent.Builder COMMAND_FISH_FINDER_NO_LOOT = Component.translatable().key("command.fish_finder.no_loot"); + TranslatableComponent.Builder COMMAND_FISH_FINDER_SPLIT_CHAR = Component.translatable().key("command.fish_finder.split_char"); + TranslatableComponent.Builder COMMAND_COMPETITION_FAILURE_NOT_EXIST = Component.translatable().key("command.competition.failure.not_exist"); + TranslatableComponent.Builder COMMAND_COMPETITION_FAILURE_NO_COMPETITION = Component.translatable().key("command.competition.failure.no_competition"); + TranslatableComponent.Builder COMMAND_COMPETITION_START_SUCCESS = Component.translatable().key("command.competition.start.success"); + TranslatableComponent.Builder COMMAND_COMPETITION_STOP_SUCCESS = Component.translatable().key("command.competition.stop.success"); + TranslatableComponent.Builder COMMAND_COMPETITION_END_SUCCESS = Component.translatable().key("command.competition.end.success"); + TranslatableComponent.Builder COMMAND_BAG_EDIT_FAILURE_UNSAFE = Component.translatable().key("command.bag.edit.failure.unsafe"); + TranslatableComponent.Builder COMMAND_BAG_EDIT_FAILURE_NEVER_PLAYED = Component.translatable().key("command.bag.edit.failure.never_played"); + TranslatableComponent.Builder COMMAND_BAG_OPEN_SUCCESS = Component.translatable().key("command.bag.open.success"); + TranslatableComponent.Builder COMMAND_BAG_OPEN_FAILURE_NOT_LOADED = Component.translatable().key("command.bag.open.failure.not_loaded"); + TranslatableComponent.Builder COMMAND_DATA_FAILURE_NOT_LOADED = Component.translatable().key("command.data.failure.not_loaded"); + TranslatableComponent.Builder COMMAND_MARKET_OPEN_SUCCESS = Component.translatable().key("command.market.open.success"); + TranslatableComponent.Builder COMMAND_MARKET_OPEN_FAILURE_NOT_LOADED = Component.translatable().key("command.market.open.failure.not_loaded"); + TranslatableComponent.Builder COMMAND_DATA_UNLOCK_SUCCESS = Component.translatable().key("command.data.unlock.success"); + TranslatableComponent.Builder COMMAND_DATA_IMPORT_FAILURE_NOT_EXISTS = Component.translatable().key("command.data.import.failure.not_exists"); + TranslatableComponent.Builder COMMAND_DATA_IMPORT_FAILURE_PLAYER_ONLINE = Component.translatable().key("command.data.import.failure.player_online"); + TranslatableComponent.Builder COMMAND_DATA_IMPORT_FAILURE_INVALID_FILE = Component.translatable().key("command.data.import.failure.invalid_file"); + TranslatableComponent.Builder COMMAND_DATA_IMPORT_START = Component.translatable().key("command.data.import.start"); + TranslatableComponent.Builder COMMAND_DATA_IMPORT_PROGRESS = Component.translatable().key("command.data.import.progress"); + TranslatableComponent.Builder COMMAND_DATA_IMPORT_SUCCESS = Component.translatable().key("command.data.import.success"); + TranslatableComponent.Builder COMMAND_DATA_EXPORT_FAILURE_PLAYER_ONLINE = Component.translatable().key("command.data.export.failure.player_online"); + TranslatableComponent.Builder COMMAND_DATA_EXPORT_START = Component.translatable().key("command.data.export.start"); + TranslatableComponent.Builder COMMAND_DATA_EXPORT_PROGRESS = Component.translatable().key("command.data.export.progress"); + TranslatableComponent.Builder COMMAND_DATA_EXPORT_SUCCESS = Component.translatable().key("command.data.export.success"); + TranslatableComponent.Builder COMMAND_STATISTICS_FAILURE_NOT_LOADED = Component.translatable().key("command.statistics.failure.not_loaded"); + TranslatableComponent.Builder COMMAND_STATISTICS_FAILURE_UNSUPPORTED = Component.translatable().key("command.statistics.failure.unsupported"); + TranslatableComponent.Builder COMMAND_STATISTICS_MODIFY_SUCCESS = Component.translatable().key("command.statistics.modify.success"); + TranslatableComponent.Builder COMMAND_STATISTICS_RESET_SUCCESS = Component.translatable().key("command.statistics.reset.success"); + TranslatableComponent.Builder COMMAND_STATISTICS_QUERY_AMOUNT = Component.translatable().key("command.statistics.query.amount"); + TranslatableComponent.Builder COMMAND_STATISTICS_QUERY_SIZE = Component.translatable().key("command.statistics.query.size"); + +// TranslatableComponent.Builder GUI_SELECT_FILE = Component.translatable().key("gui.select_file"); +// TranslatableComponent.Builder GUI_SELECT_ITEM = Component.translatable().key("gui.select_item"); +// TranslatableComponent.Builder GUI_INVALID_KEY = Component.translatable().key("gui.invalid_key"); +// TranslatableComponent.Builder GUI_NEW_VALUE = Component.translatable().key("gui.new_value"); +// TranslatableComponent.Builder GUI_TEMP_NEW_KEY = Component.translatable().key("gui.temp_new_key"); +// TranslatableComponent.Builder GUI_SET_NEW_KEY = Component.translatable().key("gui.set_new_key"); +// TranslatableComponent.Builder GUI_EDIT_KEY = Component.translatable().key("gui.edit_key"); +// TranslatableComponent.Builder GUI_DELETE_PROPERTY = Component.translatable().key("gui.delete_property"); +// TranslatableComponent.Builder GUI_CLICK_CONFIRM = Component.translatable().key("gui.click_confirm"); +// TranslatableComponent.Builder GUI_INVALID_NUMBER = Component.translatable().key("gui.invalid_number"); +// TranslatableComponent.Builder GUI_ILLEGAL_FORMAT = Component.translatable().key("gui.illegal_format"); +// TranslatableComponent.Builder GUI_SCROLL_UP = Component.translatable().key("gui.scroll_up"); +// TranslatableComponent.Builder GUI_SCROLL_DOWN = Component.translatable().key("gui.scroll_down"); +// TranslatableComponent.Builder GUI_CANNOT_SCROLL_UP = Component.translatable().key("gui.cannot_scroll_up"); +// TranslatableComponent.Builder GUI_CANNOT_SCROLL_DOWN = Component.translatable().key("gui.cannot_scroll_down"); +// TranslatableComponent.Builder GUI_NEXT_PAGE = Component.translatable().key("gui.next_page"); +// TranslatableComponent.Builder GUI_GOTO_NEXT_PAGE = Component.translatable().key("gui.goto_next_page"); +// TranslatableComponent.Builder GUI_CANNOT_GOTO_NEXT_PAGE = Component.translatable().key("gui.cannot_goto_next_page"); +// TranslatableComponent.Builder GUI_PREVIOUS_PAGE = Component.translatable().key("gui.previous_page"); +// TranslatableComponent.Builder GUI_GOTO_PREVIOUS_PAGE = Component.translatable().key("gui.goto_previous_page"); +// TranslatableComponent.Builder GUI_CANNOT_GOTO_PREVIOUS_PAGE = Component.translatable().key("gui.cannot_goto_previous_page"); +// TranslatableComponent.Builder GUI_BACK_TO_PARENT_PAGE = Component.translatable().key("gui.back_to_parent_page"); +// TranslatableComponent.Builder GUI_BACK_TO_PARENT_FOLDER = Component.translatable().key("gui.back_to_parent_folder"); +// TranslatableComponent.Builder GUI_CURRENT_VALUE = Component.translatable().key("gui.current_value"); +// TranslatableComponent.Builder GUI_CLICK_TO_TOGGLE = Component.translatable().key("gui.click_to_toggle"); +// TranslatableComponent.Builder GUI_LEFT_CLICK_EDIT = Component.translatable().key("gui.left_click_edit"); +// TranslatableComponent.Builder GUI_RIGHT_CLICK_RESET = Component.translatable().key("gui.right_click_reset"); +// TranslatableComponent.Builder GUI_RIGHT_CLICK_DELETE = Component.translatable().key("gui.right_click_delete"); +// TranslatableComponent.Builder GUI_RIGHT_CLICK_CANCEL = Component.translatable().key("gui.right_click_cancel"); +// TranslatableComponent.Builder GUI_LOOT_SHOW_IN_FINDER = Component.translatable().key("gui.loot_show_in_finder"); +// TranslatableComponent.Builder GUI_LOOT_SCORE = Component.translatable().key("gui.loot_score"); +// TranslatableComponent.Builder GUI_LOOT_NICK = Component.translatable().key("gui.loot_nick"); +// TranslatableComponent.Builder GUI_LOOT_INSTANT_GAME = Component.translatable().key("gui.loot_instant_game"); +// TranslatableComponent.Builder GUI_LOOT_DISABLE_STATISTICS = Component.translatable().key("gui.loot_disable_statistics"); +// TranslatableComponent.Builder GUI_LOOT_DISABLE_GAME = Component.translatable().key("gui.loot_disable_game"); +// TranslatableComponent.Builder GUI_ITEM_AMOUNT = Component.translatable().key("gui.item_amount"); +// TranslatableComponent.Builder GUI_ITEM_CUSTOM_MODEL_DATA = Component.translatable().key("gui.item_custom_model_data"); +// TranslatableComponent.Builder GUI_ITEM_DISPLAY_NAME = Component.translatable().key("gui.item_display_name"); +// TranslatableComponent.Builder GUI_ITEM_CUSTOM_DURABILITY = Component.translatable().key("gui.item_custom_durability"); +// TranslatableComponent.Builder GUI_ITEM_ENCHANTMENT = Component.translatable().key("gui.item_enchantment"); +// TranslatableComponent.Builder GUI_ITEM_HEAD64 = Component.translatable().key("gui.item_head64"); +// TranslatableComponent.Builder GUI_ITEM_FLAG = Component.translatable().key("gui.item_item_flag"); +// TranslatableComponent.Builder GUI_ITEM_LORE = Component.translatable().key("gui.item_lore"); +// TranslatableComponent.Builder GUI_ITEM_MATERIAL = Component.translatable().key("gui.item_material"); +// TranslatableComponent.Builder GUI_ITEM_NBT = Component.translatable().key("gui.item_nbt"); +// TranslatableComponent.Builder GUI_ITEM_PREVENT_GRAB = Component.translatable().key("gui.item_prevent_grab"); +// TranslatableComponent.Builder GUI_ITEM_PRICE = Component.translatable().key("gui.item_price"); +// TranslatableComponent.Builder GUI_ITEM_PRICE_BASE = Component.translatable().key("gui.item_price_base"); +// TranslatableComponent.Builder GUI_ITEM_PRICE_BONUS = Component.translatable().key("gui.item_price_bonus"); +// TranslatableComponent.Builder GUI_ITEM_RANDOM_DURABILITY = Component.translatable().key("gui.item_random_durability"); +// TranslatableComponent.Builder GUI_ITEM_SIZE = Component.translatable().key("gui.item_size"); +// TranslatableComponent.Builder GUI_ITEM_STACKABLE = Component.translatable().key("gui.item_stackable"); +// TranslatableComponent.Builder GUI_ITEM_STORED_ENCHANTMENT = Component.translatable().key("gui.item_stored_enchantment"); +// TranslatableComponent.Builder GUI_ITEM_TAG = Component.translatable().key("gui.item_tag"); +// TranslatableComponent.Builder GUI_ITEM_UNBREAKABLE = Component.translatable().key("gui.item_unbreakable"); +// TranslatableComponent.Builder GUI_PAGE_AMOUNT_TITLE = Component.translatable().key("gui.page_amount_title"); +// TranslatableComponent.Builder GUI_PAGE_MODEL_DATA_TITLE = Component.translatable().key("gui.page_model_data_title"); +// TranslatableComponent.Builder GUI_PAGE_DISPLAY_NAME_TITLE = Component.translatable().key("gui.page_display_name_title"); +// TranslatableComponent.Builder GUI_PAGE_NEW_DISPLAY_NAME = Component.translatable().key("gui.page_new_display_name"); +// TranslatableComponent.Builder GUI_PAGE_CUSTOM_DURABILITY_TITLE = Component.translatable().key("gui.page_custom_durability_title"); +// TranslatableComponent.Builder GUI_PAGE_STORED_ENCHANTMENT_TITLE = Component.translatable().key("gui.page_stored_enchantment_title"); +// TranslatableComponent.Builder GUI_PAGE_ENCHANTMENT_TITLE = Component.translatable().key("gui.page_enchantment_title"); +// TranslatableComponent.Builder GUI_PAGE_SELECT_ONE_ENCHANTMENT = Component.translatable().key("gui.page_select_one_enchantment"); +// TranslatableComponent.Builder GUI_PAGE_ADD_NEW_ENCHANTMENT = Component.translatable().key("gui.page_add_new_enchantment"); +// TranslatableComponent.Builder GUI_PAGE_ITEM_FLAG_TITLE = Component.translatable().key("gui.page_item_flag_title"); +// TranslatableComponent.Builder GUI_PAGE_LORE_TITLE = Component.translatable().key("gui.page_lore_title"); +// TranslatableComponent.Builder GUI_PAGE_ADD_NEW_LORE = Component.translatable().key("gui.page_add_new_lore"); +// TranslatableComponent.Builder GUI_PAGE_SELECT_ONE_LORE = Component.translatable().key("gui.page_select_one_lore"); +// TranslatableComponent.Builder GUI_PAGE_MATERIAL_TITLE = Component.translatable().key("gui.page_material_title"); +// TranslatableComponent.Builder GUI_PAGE_NBT_COMPOUND_KEY_TITLE = Component.translatable().key("gui.page_nbt_compound_key_title"); +// TranslatableComponent.Builder GUI_PAGE_NBT_LIST_KEY_TITLE = Component.translatable().key("gui.page_nbt_list_key_title"); +// TranslatableComponent.Builder GUI_PAGE_NBT_KEY_TITLE = Component.translatable().key("gui.page_nbt_key_title"); +// TranslatableComponent.Builder GUI_PAGE_NBT_INVALID_KEY = Component.translatable().key("gui.page_nbt_invalid_key"); +// TranslatableComponent.Builder GUI_PAGE_NBT_ADD_NEW_COMPOUND= Component.translatable().key("gui.page_nbt_add_new_compound"); +// TranslatableComponent.Builder GUI_PAGE_NBT_ADD_NEW_LIST = Component.translatable().key("gui.page_nbt_add_new_list"); +// TranslatableComponent.Builder GUI_PAGE_NBT_ADD_NEW_VALUE = Component.translatable().key("gui.page_nbt_add_new_value"); +// TranslatableComponent.Builder GUI_PAGE_ADD_NEW_KEY = Component.translatable().key("gui.page_add_new_key"); +// TranslatableComponent.Builder GUI_PAGE_NBT_PREVIEW = Component.translatable().key("gui.page_nbt_preview"); +// TranslatableComponent.Builder GUI_PAGE_NBT_BACK_TO_COMPOUND = Component.translatable().key("gui.page_nbt_back_to_compound"); +// TranslatableComponent.Builder GUI_PAGE_NBT_SET_VALUE_TITLE = Component.translatable().key("gui.page_nbt_set_value_title"); +// TranslatableComponent.Builder GUI_PAGE_NBT_EDIT_TITLE = Component.translatable().key("gui.page_nbt_edit_title"); +// TranslatableComponent.Builder GUI_PAGE_NICK_TITLE = Component.translatable().key("gui.page_nick_title"); +// TranslatableComponent.Builder GUI_PAGE_NEW_NICK = Component.translatable().key("gui.page_new_nick"); +// TranslatableComponent.Builder GUI_PAGE_PRICE_TITLE = Component.translatable().key("gui.page_price_title"); +// TranslatableComponent.Builder GUI_PAGE_BASE_PRICE = Component.translatable().key("gui.page_base_price"); +// TranslatableComponent.Builder GUI_PAGE_BASE_BONUS = Component.translatable().key("gui.page_base_bonus"); +// TranslatableComponent.Builder GUI_PAGE_SCORE_TITLE = Component.translatable().key("gui.page_score_title"); +// TranslatableComponent.Builder GUI_PAGE_SIZE_TITLE = Component.translatable().key("gui.page_size_title"); +// TranslatableComponent.Builder GUI_PAGE_SIZE_MIN = Component.translatable().key("gui.page_size_min"); +// TranslatableComponent.Builder GUI_PAGE_SIZE_MAX = Component.translatable().key("gui.page_size_max"); +// TranslatableComponent.Builder GUI_PAGE_SIZE_MAX_NO_LESS_MIN = Component.translatable().key("gui.page_size_max_no_less_min"); +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/locale/MiniMessageTranslationRegistry.java b/common/src/main/java/net/momirealms/customfishing/common/locale/MiniMessageTranslationRegistry.java new file mode 100644 index 00000000..afa376fa --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/locale/MiniMessageTranslationRegistry.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.locale; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.translation.Translator; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; + +public interface MiniMessageTranslationRegistry extends Translator { + + static @NotNull MiniMessageTranslationRegistry create(final Key name, final MiniMessage miniMessage) { + return new MiniMessageTranslationRegistryImpl(requireNonNull(name, "name"), requireNonNull(miniMessage, "MiniMessage")); + } + + void register(@NotNull String key, @NotNull Locale locale, @NotNull String format); + + void unregister(@NotNull String key); + + boolean contains(@NotNull String key); + + String miniMessageTranslation(@NotNull String key, @NotNull Locale locale); + + void defaultLocale(@NotNull Locale defaultLocale); + + default void registerAll(final @NotNull Locale locale, final @NotNull Map bundle) { + this.registerAll(locale, bundle.keySet(), bundle::get); + } + + default void registerAll(final @NotNull Locale locale, final @NotNull Set keys, final Function function) { + IllegalArgumentException firstError = null; + int errorCount = 0; + for (final String key : keys) { + try { + this.register(key, locale, function.apply(key)); + } catch (final IllegalArgumentException e) { + if (firstError == null) { + firstError = e; + } + errorCount++; + } + } + if (firstError != null) { + if (errorCount == 1) { + throw firstError; + } else if (errorCount > 1) { + throw new IllegalArgumentException(String.format("Invalid key (and %d more)", errorCount - 1), firstError); + } + } + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/locale/MiniMessageTranslationRegistryImpl.java b/common/src/main/java/net/momirealms/customfishing/common/locale/MiniMessageTranslationRegistryImpl.java new file mode 100644 index 00000000..3652e6a9 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/locale/MiniMessageTranslationRegistryImpl.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.locale; + +import net.kyori.adventure.internal.Internals; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.minimessage.Context; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.ParsingException; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.kyori.adventure.util.TriState; +import net.kyori.examination.Examinable; +import net.kyori.examination.ExaminableProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.text.MessageFormat; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +public class MiniMessageTranslationRegistryImpl implements Examinable, MiniMessageTranslationRegistry { + private final Key name; + private final Map translations = new ConcurrentHashMap<>(); + private Locale defaultLocale = Locale.US; + private final MiniMessage miniMessage; + + MiniMessageTranslationRegistryImpl(final Key name, final MiniMessage miniMessage) { + this.name = name; + this.miniMessage = miniMessage; + } + + @Override + public void register(final @NotNull String key, final @NotNull Locale locale, final @NotNull String format) { + this.translations.computeIfAbsent(key, Translation::new).register(locale, format); + } + + @Override + public void unregister(final @NotNull String key) { + this.translations.remove(key); + } + + @Override + public boolean contains(final @NotNull String key) { + return this.translations.containsKey(key); + } + + @Override + public @NotNull Key name() { + return name; + } + + @Override + public @Nullable MessageFormat translate(@NotNull String key, @NotNull Locale locale) { + // No need to implement this method + return null; + } + + @Override + public @Nullable Component translate(@NotNull TranslatableComponent component, @NotNull Locale locale) { + Translation translation = translations.get(component.key()); + if (translation == null) { + return null; + } + String miniMessageString = translation.translate(locale); + if (miniMessageString == null) { + return null; + } + if (miniMessageString.isEmpty()) { + return Component.empty(); + } + final Component resultingComponent; + if (component.arguments().isEmpty()) { + resultingComponent = this.miniMessage.deserialize(miniMessageString); + } else { + resultingComponent = this.miniMessage.deserialize(miniMessageString, new ArgumentTag(component.arguments())); + } + if (component.children().isEmpty()) { + return resultingComponent; + } else { + return resultingComponent.children(component.children()); + } + } + + @Override + public String miniMessageTranslation(@NotNull String key, @NotNull Locale locale) { + Translation translation = translations.get(key); + if (translation == null) { + return null; + } + return translation.translate(locale); + } + + @Override + public @NotNull TriState hasAnyTranslations() { + if (!this.translations.isEmpty()) { + return TriState.TRUE; + } + return TriState.FALSE; + } + + @Override + public void defaultLocale(final @NotNull Locale defaultLocale) { + this.defaultLocale = requireNonNull(defaultLocale, "defaultLocale"); + } + + @Override + public @NotNull Stream examinableProperties() { + return Stream.of(ExaminableProperty.of("translations", this.translations)); + } + + @Override + public boolean equals(final Object other) { + if (this == other) return true; + if (!(other instanceof MiniMessageTranslationRegistryImpl that)) return false; + return this.name.equals(that.name) + && this.translations.equals(that.translations) + && this.defaultLocale.equals(that.defaultLocale); + } + + @Override + public int hashCode() { + return Objects.hash(this.name, this.translations, this.defaultLocale); + } + + @Override + public String toString() { + return Internals.toString(this); + } + + public static class ArgumentTag implements TagResolver { + private static final String NAME = "argument"; + private static final String NAME_1 = "arg"; + + private final List argumentComponents; + + public ArgumentTag(final @NotNull List argumentComponents) { + this.argumentComponents = Objects.requireNonNull(argumentComponents, "argumentComponents"); + } + + @Override + public @Nullable Tag resolve(final @NotNull String name, final @NotNull ArgumentQueue arguments, final @NotNull Context ctx) throws ParsingException { + final int index = arguments.popOr("No argument number provided").asInt().orElseThrow(() -> ctx.newException("Invalid argument number", arguments)); + + if (index < 0 || index >= argumentComponents.size()) { + throw ctx.newException("Invalid argument number", arguments); + } + + return Tag.inserting(argumentComponents.get(index)); + } + + @Override + public boolean has(final @NotNull String name) { + return name.equals(NAME) || name.equals(NAME_1); + } + } + + final class Translation implements Examinable { + private final String key; + private final Map formats; + + Translation(final @NotNull String key) { + this.key = requireNonNull(key, "translation key"); + this.formats = new ConcurrentHashMap<>(); + } + + void register(final @NotNull Locale locale, final @NotNull String format) { + if (this.formats.putIfAbsent(requireNonNull(locale, "locale"), requireNonNull(format, "message format")) != null) { + throw new IllegalArgumentException(String.format("Translation already exists: %s for %s", this.key, locale)); + } + } + + @Nullable String translate(final @NotNull Locale locale) { + String format = this.formats.get(requireNonNull(locale, "locale")); + if (format == null) { + format = this.formats.get(new Locale(locale.getLanguage())); // try without country + if (format == null) { + format = this.formats.get(MiniMessageTranslationRegistryImpl.this.defaultLocale); // try local default locale + } + } + return format; + } + + @Override + public @NotNull Stream examinableProperties() { + return Stream.of( + ExaminableProperty.of("key", this.key), + ExaminableProperty.of("formats", this.formats) + ); + } + + @Override + public boolean equals(final Object other) { + if (this == other) return true; + if (!(other instanceof Translation that)) return false; + return this.key.equals(that.key) && + this.formats.equals(that.formats); + } + + @Override + public int hashCode() { + return Objects.hash(this.key, this.formats); + } + + @Override + public String toString() { + return Internals.toString(this); + } + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/locale/MiniMessageTranslator.java b/common/src/main/java/net/momirealms/customfishing/common/locale/MiniMessageTranslator.java new file mode 100644 index 00000000..47c42114 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/locale/MiniMessageTranslator.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.locale; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; +import net.kyori.adventure.translation.Translator; +import net.kyori.examination.Examinable; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; + +public interface MiniMessageTranslator extends Translator, Examinable { + + static @NotNull MiniMessageTranslator translator() { + return MiniMessageTranslatorImpl.INSTANCE; + } + + static @NotNull TranslatableComponentRenderer renderer() { + return MiniMessageTranslatorImpl.INSTANCE.renderer; + } + + static @NotNull Component render(final @NotNull Component component, final @NotNull Locale locale) { + return renderer().render(component, locale); + } + + @NotNull Iterable sources(); + + boolean addSource(final @NotNull Translator source); + + boolean removeSource(final @NotNull Translator source); +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/locale/MiniMessageTranslatorImpl.java b/common/src/main/java/net/momirealms/customfishing/common/locale/MiniMessageTranslatorImpl.java new file mode 100644 index 00000000..ea55ec5c --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/locale/MiniMessageTranslatorImpl.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.locale; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; +import net.kyori.adventure.translation.Translator; +import net.kyori.adventure.util.TriState; +import net.kyori.examination.ExaminableProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.text.MessageFormat; +import java.util.Collections; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +public class MiniMessageTranslatorImpl implements MiniMessageTranslator { + + private static final Key NAME = Key.key("customfishing", "main"); + static final MiniMessageTranslatorImpl INSTANCE = new MiniMessageTranslatorImpl(); + final TranslatableComponentRenderer renderer = TranslatableComponentRenderer.usingTranslationSource(this); + private final Set sources = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + @Override + public @NotNull Key name() { + return NAME; + } + + @Override + public @NotNull TriState hasAnyTranslations() { + if (!this.sources.isEmpty()) { + return TriState.TRUE; + } + return TriState.FALSE; + } + + @Override + public @Nullable MessageFormat translate(@NotNull String key, @NotNull Locale locale) { + // No need to implement this method + return null; + } + + @Override + public @Nullable Component translate(@NotNull TranslatableComponent component, @NotNull Locale locale) { + for (final Translator source : this.sources) { + final Component translation = source.translate(component, locale); + if (translation != null) return translation; + } + return null; + } + + @Override + public @NotNull Iterable sources() { + return Collections.unmodifiableSet(this.sources); + } + + @Override + public boolean addSource(final @NotNull Translator source) { + if (source == this) throw new IllegalArgumentException("MiniMessageTranslationSource"); + return this.sources.add(source); + } + + @Override + public boolean removeSource(final @NotNull Translator source) { + return this.sources.remove(source); + } + + @Override + public @NotNull Stream examinableProperties() { + return Stream.of(ExaminableProperty.of("sources", this.sources)); + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/locale/TranslationManager.java b/common/src/main/java/net/momirealms/customfishing/common/locale/TranslationManager.java new file mode 100644 index 00000000..13f33ab3 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/locale/TranslationManager.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.locale; + +import dev.dejvokep.boostedyaml.YamlDocument; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.translation.Translator; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import net.momirealms.customfishing.common.util.Pair; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class TranslationManager { + + public static final Locale DEFAULT_LOCALE = Locale.ENGLISH; + private static final List locales = List.of("en"); + private static TranslationManager instance; + + private final CustomFishingPlugin plugin; + private final Set installed = ConcurrentHashMap.newKeySet(); + private MiniMessageTranslationRegistry registry; + private final Path translationsDirectory; + + public TranslationManager(CustomFishingPlugin plugin) { + this.plugin = plugin; + this.translationsDirectory = this.plugin.getConfigDirectory().resolve("translations"); + instance = this; + } + + public void reload() { + // remove any previous registry + if (this.registry != null) { + MiniMessageTranslator.translator().removeSource(this.registry); + this.installed.clear(); + } + + for (String lang : locales) { + this.plugin.getConfigManager().saveResource("translations/" + lang + ".yml"); + } + + this.registry = MiniMessageTranslationRegistry.create(Key.key("customfishing", "main"), AdventureHelper.getMiniMessage()); + this.registry.defaultLocale(DEFAULT_LOCALE); + this.loadFromFileSystem(this.translationsDirectory, false); + MiniMessageTranslator.translator().addSource(this.registry); + } + + public static String miniMessageTranslation(String key) { + return miniMessageTranslation(key, null); + } + + public static String miniMessageTranslation(String key, @Nullable Locale locale) { + if (locale == null) { + locale = Locale.getDefault(); + if (locale == null) { + locale = DEFAULT_LOCALE; + } + } + return instance.registry.miniMessageTranslation(key, locale); + } + + public static Component render(Component component) { + return render(component, null); + } + + public static Component render(Component component, @Nullable Locale locale) { + if (locale == null) { + locale = Locale.getDefault(); + if (locale == null) { + locale = DEFAULT_LOCALE; + } + } + return MiniMessageTranslator.render(component, locale); + } + + public void loadFromFileSystem(Path directory, boolean suppressDuplicatesError) { + List translationFiles; + try (Stream stream = Files.list(directory)) { + translationFiles = stream.filter(TranslationManager::isTranslationFile).collect(Collectors.toList()); + } catch (IOException e) { + translationFiles = Collections.emptyList(); + } + + if (translationFiles.isEmpty()) { + return; + } + + Map> loaded = new HashMap<>(); + for (Path translationFile : translationFiles) { + try { + Pair> result = loadTranslationFile(translationFile); + loaded.put(result.left(), result.right()); + } catch (Exception e) { + if (!suppressDuplicatesError || !isAdventureDuplicatesException(e)) { + this.plugin.getPluginLogger().warn("Error loading locale file: " + translationFile.getFileName(), e); + } + } + } + + // try registering the locale without a country code - if we don't already have a registration for that + loaded.forEach((locale, bundle) -> { + Locale localeWithoutCountry = new Locale(locale.getLanguage()); + if (!locale.equals(localeWithoutCountry) && !localeWithoutCountry.equals(DEFAULT_LOCALE) && this.installed.add(localeWithoutCountry)) { + try { + this.registry.registerAll(localeWithoutCountry, bundle); + } catch (IllegalArgumentException e) { + // ignore + } + } + }); + + Locale localLocale = Locale.getDefault(); + if (!this.installed.contains(localLocale)) { + plugin.getPluginLogger().warn(localLocale.toString().toLowerCase(Locale.ENGLISH) + ".yml not exists, using en.yml as default locale."); + } + } + + public static boolean isTranslationFile(Path path) { + return path.getFileName().toString().endsWith(".yml"); + } + + private static boolean isAdventureDuplicatesException(Exception e) { + return e instanceof IllegalArgumentException && (e.getMessage().startsWith("Invalid key") || e.getMessage().startsWith("Translation already exists")); + } + + @SuppressWarnings("unchecked") + private Pair> loadTranslationFile(Path translationFile) { + String fileName = translationFile.getFileName().toString(); + String localeString = fileName.substring(0, fileName.length() - ".yml".length()); + Locale locale = parseLocale(localeString); + + if (locale == null) { + throw new IllegalStateException("Unknown locale '" + localeString + "' - unable to register."); + } + + Map bundle = new HashMap<>(); + YamlDocument document = plugin.getConfigManager().loadConfig("translations" + "\\" + translationFile.getFileName(), '@'); + Map map = document.getStringRouteMappedValues(false); + map.remove("config-version"); + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() instanceof List list) { + List strList = (List) list; + StringJoiner stringJoiner = new StringJoiner(""); + for (String str : strList) { + stringJoiner.add(str); + } + bundle.put(entry.getKey(), stringJoiner.toString()); + } else if (entry.getValue() instanceof String str) { + bundle.put(entry.getKey(), str); + } + } + + this.registry.registerAll(locale, bundle); + this.installed.add(locale); + + return Pair.of(locale, bundle); + } + + public static @Nullable Locale parseLocale(@Nullable String locale) { + return locale == null ? null : Translator.parseLocale(locale); + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/plugin/CustomFishingPlugin.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/CustomFishingPlugin.java new file mode 100644 index 00000000..e4fa118b --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/CustomFishingPlugin.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.plugin; + +import net.momirealms.customfishing.common.config.ConfigLoader; +import net.momirealms.customfishing.common.dependency.DependencyManager; +import net.momirealms.customfishing.common.locale.TranslationManager; +import net.momirealms.customfishing.common.plugin.classpath.ClassPathAppender; +import net.momirealms.customfishing.common.plugin.logging.PluginLogger; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerAdapter; + +import java.io.InputStream; +import java.nio.file.Path; + +public interface CustomFishingPlugin { + + InputStream getResourceStream(String filePath); + + PluginLogger getPluginLogger(); + + ClassPathAppender getClassPathAppender(); + + SchedulerAdapter getScheduler(); + + Path getDataDirectory(); + + default Path getConfigDirectory() { + return getDataDirectory(); + } + + DependencyManager getDependencyManager(); + + TranslationManager getTranslationManager(); + + ConfigLoader getConfigManager(); + + String getServerVersion(); + + String getPluginVersion(); +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/classpath/ClassPathAppender.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/classpath/ClassPathAppender.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/classpath/ClassPathAppender.java rename to common/src/main/java/net/momirealms/customfishing/common/plugin/classpath/ClassPathAppender.java index 601853a7..71c41416 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/classpath/ClassPathAppender.java +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/classpath/ClassPathAppender.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.classpath; +package net.momirealms.customfishing.common.plugin.classpath; import java.nio.file.Path; diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/classpath/ReflectionClassPathAppender.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/classpath/ReflectionClassPathAppender.java similarity index 87% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/classpath/ReflectionClassPathAppender.java rename to common/src/main/java/net/momirealms/customfishing/common/plugin/classpath/ReflectionClassPathAppender.java index 5732f66a..277ebfed 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/classpath/ReflectionClassPathAppender.java +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/classpath/ReflectionClassPathAppender.java @@ -23,7 +23,9 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.classpath; +package net.momirealms.customfishing.common.plugin.classpath; + +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; import java.net.MalformedURLException; import java.net.URLClassLoader; @@ -40,6 +42,10 @@ public class ReflectionClassPathAppender implements ClassPathAppender { } } + public ReflectionClassPathAppender(CustomFishingPlugin plugin) throws IllegalStateException { + this(plugin.getClass().getClassLoader()); + } + @Override public void addJarToClasspath(Path file) { try { diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/classpath/URLClassLoaderAccess.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/classpath/URLClassLoaderAccess.java similarity index 95% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/classpath/URLClassLoaderAccess.java rename to common/src/main/java/net/momirealms/customfishing/common/plugin/classpath/URLClassLoaderAccess.java index 1054b247..e62907f3 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/classpath/URLClassLoaderAccess.java +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/classpath/URLClassLoaderAccess.java @@ -23,9 +23,9 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.classpath; +package net.momirealms.customfishing.common.plugin.classpath; -import org.checkerframework.checker.nullness.qual.NonNull; +import org.jetbrains.annotations.NotNull; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -66,7 +66,7 @@ public abstract class URLClassLoaderAccess { * * @param url the URL to add */ - public abstract void addURL(@NonNull URL url); + public abstract void addURL(@NotNull URL url); private static void throwError(Throwable cause) throws UnsupportedOperationException { throw new UnsupportedOperationException("CustomFishing is unable to inject into the plugin URLClassLoader.\n" + @@ -100,7 +100,7 @@ public abstract class URLClassLoaderAccess { } @Override - public void addURL(@NonNull URL url) { + public void addURL(@NotNull URL url) { try { ADD_URL_METHOD.invoke(super.classLoader, url); } catch (ReflectiveOperationException e) { @@ -162,7 +162,7 @@ public abstract class URLClassLoaderAccess { } @Override - public void addURL(@NonNull URL url) { + public void addURL(@NotNull URL url) { if (this.unopenedURLs == null || this.pathURLs == null) { URLClassLoaderAccess.throwError(new NullPointerException("unopenedURLs or pathURLs")); } @@ -182,7 +182,7 @@ public abstract class URLClassLoaderAccess { } @Override - public void addURL(@NonNull URL url) { + public void addURL(@NotNull URL url) { URLClassLoaderAccess.throwError(null); } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntitySettings.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/feature/Reloadable.java similarity index 71% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntitySettings.java rename to common/src/main/java/net/momirealms/customfishing/common/plugin/feature/Reloadable.java index 57ae5d76..6e7efc82 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntitySettings.java +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/feature/Reloadable.java @@ -15,18 +15,22 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.mechanic.entity; +package net.momirealms.customfishing.common.plugin.feature; -import java.util.Map; +public interface Reloadable { -public interface EntitySettings { - boolean isPersist(); + default void reload() { + unload(); + load(); + } - double getHorizontalVector(); + default void unload() { + } - double getVerticalVector(); + default void load() { + } - String getEntityID(); - - Map getPropertyMap(); + default void disable() { + unload(); + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/classpath/JarInJarClassPathAppender.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/logging/JavaPluginLogger.java similarity index 54% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/classpath/JarInJarClassPathAppender.java rename to common/src/main/java/net/momirealms/customfishing/common/plugin/logging/JavaPluginLogger.java index 7d8b30ee..70c74c86 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/classpath/JarInJarClassPathAppender.java +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/logging/JavaPluginLogger.java @@ -23,40 +23,40 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.classpath; +package net.momirealms.customfishing.common.plugin.logging; -import net.momirealms.customfishing.libraries.loader.JarInJarClassLoader; +import java.util.logging.Level; +import java.util.logging.Logger; -import java.io.IOException; -import java.net.MalformedURLException; -import java.nio.file.Path; +public class JavaPluginLogger implements PluginLogger { + private final Logger logger; -public class JarInJarClassPathAppender implements ClassPathAppender { - private final JarInJarClassLoader classLoader; - - public JarInJarClassPathAppender(ClassLoader classLoader) { - if (!(classLoader instanceof JarInJarClassLoader)) { - throw new IllegalArgumentException("Loader is not a JarInJarClassLoader: " + classLoader.getClass().getName()); - } - this.classLoader = (JarInJarClassLoader) classLoader; + public JavaPluginLogger(Logger logger) { + this.logger = logger; } @Override - public void addJarToClasspath(Path file) { - try { - this.classLoader.addJarToClasspath(file.toUri().toURL()); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } + public void info(String s) { + this.logger.info(s); } @Override - public void close() { - this.classLoader.deleteJarResource(); - try { - this.classLoader.close(); - } catch (IOException e) { - e.printStackTrace(); - } + public void warn(String s) { + this.logger.warning(s); + } + + @Override + public void warn(String s, Throwable t) { + this.logger.log(Level.WARNING, s, t); + } + + @Override + public void severe(String s) { + this.logger.severe(s); + } + + @Override + public void severe(String s, Throwable t) { + this.logger.log(Level.SEVERE, s, t); } } diff --git a/common/src/main/java/net/momirealms/customfishing/common/plugin/logging/Log4jPluginLogger.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/logging/Log4jPluginLogger.java new file mode 100644 index 00000000..338ab9e5 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/logging/Log4jPluginLogger.java @@ -0,0 +1,61 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.common.plugin.logging; + +import org.apache.logging.log4j.Logger; + +public class Log4jPluginLogger implements PluginLogger { + private final Logger logger; + + public Log4jPluginLogger(Logger logger) { + this.logger = logger; + } + + @Override + public void info(String s) { + this.logger.info(s); + } + + @Override + public void warn(String s) { + this.logger.warn(s); + } + + @Override + public void warn(String s, Throwable t) { + this.logger.warn(s, t); + } + + @Override + public void severe(String s) { + this.logger.error(s); + } + + @Override + public void severe(String s, Throwable t) { + this.logger.error(s, t); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/loader/LoadingException.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/logging/PluginLogger.java similarity index 71% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/loader/LoadingException.java rename to common/src/main/java/net/momirealms/customfishing/common/plugin/logging/PluginLogger.java index 26528e94..180d63ee 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/loader/LoadingException.java +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/logging/PluginLogger.java @@ -23,19 +23,24 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.loader; +package net.momirealms.customfishing.common.plugin.logging; /** - * Runtime exception used if there is a problem during loading + * Represents the logger instance being used by CustomFishing on the platform. + * + *

Messages sent using the logger are sent prefixed with the CustomFishing tag, + * and on some implementations will be colored depending on the message type.

*/ -public class LoadingException extends RuntimeException { +public interface PluginLogger { - public LoadingException(String message) { - super(message); - } + void info(String s); - public LoadingException(String message, Throwable cause) { - super(message, cause); - } + void warn(String s); + + void warn(String s, Throwable t); + + void severe(String s); + + void severe(String s, Throwable t); } diff --git a/common/src/main/java/net/momirealms/customfishing/common/plugin/logging/Slf4jPluginLogger.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/logging/Slf4jPluginLogger.java new file mode 100644 index 00000000..a77f01a6 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/logging/Slf4jPluginLogger.java @@ -0,0 +1,61 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.common.plugin.logging; + +import org.slf4j.Logger; + +public class Slf4jPluginLogger implements PluginLogger { + private final Logger logger; + + public Slf4jPluginLogger(Logger logger) { + this.logger = logger; + } + + @Override + public void info(String s) { + this.logger.info(s); + } + + @Override + public void warn(String s) { + this.logger.warn(s); + } + + @Override + public void warn(String s, Throwable t) { + this.logger.warn(s, t); + } + + @Override + public void severe(String s) { + this.logger.error(s); + } + + @Override + public void severe(String s, Throwable t) { + this.logger.error(s, t); + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/AbstractJavaScheduler.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/AbstractJavaScheduler.java new file mode 100644 index 00000000..f59b3d56 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/AbstractJavaScheduler.java @@ -0,0 +1,132 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.common.plugin.scheduler; + +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Arrays; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * Abstract implementation of {@link SchedulerAdapter} using a {@link ScheduledExecutorService}. + */ +public abstract class AbstractJavaScheduler implements SchedulerAdapter { + private static final int PARALLELISM = 16; + + private final CustomFishingPlugin plugin; + + private final ScheduledThreadPoolExecutor scheduler; + private final ForkJoinPool worker; + + public AbstractJavaScheduler(CustomFishingPlugin plugin) { + this.plugin = plugin; + + this.scheduler = new ScheduledThreadPoolExecutor(4, r -> { + Thread thread = Executors.defaultThreadFactory().newThread(r); + thread.setName("customfishing-scheduler"); + return thread; + }); + this.scheduler.setRemoveOnCancelPolicy(true); + this.scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + this.worker = new ForkJoinPool(PARALLELISM, new WorkerThreadFactory(), new ExceptionHandler(), false); + } + + @Override + public Executor async() { + return this.worker; + } + + @Override + public SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit) { + ScheduledFuture future = this.scheduler.schedule(() -> this.worker.execute(task), delay, unit); + return () -> future.cancel(false); + } + + @Override + public SchedulerTask asyncRepeating(Runnable task, long delay, long interval, TimeUnit unit) { + ScheduledFuture future = this.scheduler.scheduleAtFixedRate(() -> this.worker.execute(task), delay, interval, unit); + return () -> future.cancel(false); + } + + @Override + public void shutdownScheduler() { + this.scheduler.shutdown(); + try { + if (!this.scheduler.awaitTermination(1, TimeUnit.MINUTES)) { + this.plugin.getPluginLogger().severe("Timed out waiting for the CustomFishing scheduler to terminate"); + reportRunningTasks(thread -> thread.getName().equals("customfishing-scheduler")); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Override + public void shutdownExecutor() { + this.worker.shutdown(); + try { + if (!this.worker.awaitTermination(1, TimeUnit.MINUTES)) { + this.plugin.getPluginLogger().severe("Timed out waiting for the CustomFishing worker thread pool to terminate"); + reportRunningTasks(thread -> thread.getName().startsWith("customfishing-worker-")); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private void reportRunningTasks(Predicate predicate) { + Thread.getAllStackTraces().forEach((thread, stack) -> { + if (predicate.test(thread)) { + this.plugin.getPluginLogger().warn("Thread " + thread.getName() + " is blocked, and may be the reason for the slow shutdown!\n" + + Arrays.stream(stack).map(el -> " " + el).collect(Collectors.joining("\n")) + ); + } + }); + } + + private static final class WorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { + private static final AtomicInteger COUNT = new AtomicInteger(0); + + @Override + public ForkJoinWorkerThread newThread(ForkJoinPool pool) { + ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + thread.setDaemon(true); + thread.setName("customfishing-worker-" + COUNT.getAndIncrement()); + return thread; + } + } + + private final class ExceptionHandler implements UncaughtExceptionHandler { + @Override + public void uncaughtException(Thread t, Throwable e) { + AbstractJavaScheduler.this.plugin.getPluginLogger().warn("Thread " + t.getName() + " threw an uncaught exception", e); + } + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/scheduler/CancellableTask.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/RegionExecutor.java similarity index 71% rename from api/src/main/java/net/momirealms/customfishing/api/scheduler/CancellableTask.java rename to common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/RegionExecutor.java index 257d4a4c..320542af 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/scheduler/CancellableTask.java +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/RegionExecutor.java @@ -15,19 +15,13 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.scheduler; +package net.momirealms.customfishing.common.plugin.scheduler; -public interface CancellableTask { +public interface RegionExecutor { - /** - * Cancel the task - */ - void cancel(); + void run(Runnable r, T l); - /** - * Get if the task is cancelled or not - * - * @return cancelled or not - */ - boolean isCancelled(); + SchedulerTask runLater(Runnable r, long delayTicks, T l); + + SchedulerTask runRepeating(Runnable r, long delayTicks, long period, T l); } diff --git a/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/SchedulerAdapter.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/SchedulerAdapter.java new file mode 100644 index 00000000..bcab3d0a --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/SchedulerAdapter.java @@ -0,0 +1,106 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.common.plugin.scheduler; + +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +/** + * A scheduler for running tasks using the systems provided by the platform + */ +public interface SchedulerAdapter { + + /** + * Gets an async executor instance + * + * @return an async executor instance + */ + Executor async(); + + /** + * Gets a sync executor instance + * + * @return a sync executor instance + */ + RegionExecutor sync(); + + /** + * Executes a task async + * + * @param task the task + */ + default void executeAsync(Runnable task) { + async().execute(task); + } + + /** + * Executes a task sync + * + * @param task the task + */ + default void executeSync(Runnable task, T location) { + sync().run(task, location); + } + + default void executeSync(Runnable task) { + sync().run(task, null); + } + + /** + * Executes the given task with a delay. + * + * @param task the task + * @param delay the delay + * @param unit the unit of delay + * @return the resultant task instance + */ + SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit); + + /** + * Executes the given task repeatedly at a given interval. + * + * @param task the task + * @param interval the interval + * @param unit the unit of interval + * @return the resultant task instance + */ + SchedulerTask asyncRepeating(Runnable task, long delay, long interval, TimeUnit unit); + + /** + * Shuts down the scheduler instance. + * + *

{@link #asyncLater(Runnable, long, TimeUnit)} and {@link #asyncRepeating(Runnable, long, long, TimeUnit)}.

+ */ + void shutdownScheduler(); + + /** + * Shuts down the executor instance. + * + *

{@link #async()} and {@link #executeAsync(Runnable)}.

+ */ + void shutdownExecutor(); + +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/loader/LoaderBootstrap.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/SchedulerTask.java similarity index 84% rename from plugin/src/main/java/net/momirealms/customfishing/libraries/loader/LoaderBootstrap.java rename to common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/SchedulerTask.java index 98ddd06d..685c39ef 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/loader/LoaderBootstrap.java +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/SchedulerTask.java @@ -23,17 +23,16 @@ * SOFTWARE. */ -package net.momirealms.customfishing.libraries.loader; +package net.momirealms.customfishing.common.plugin.scheduler; /** - * Minimal bootstrap plugin, called by the loader plugin. + * Represents a scheduled task */ -public interface LoaderBootstrap { +public interface SchedulerTask { - void onLoad(); - - default void onEnable() {} - - default void onDisable() {} + /** + * Cancels the task. + */ + void cancel(); } diff --git a/common/src/main/java/net/momirealms/customfishing/common/sender/AbstractSender.java b/common/src/main/java/net/momirealms/customfishing/common/sender/AbstractSender.java new file mode 100644 index 00000000..8b7e0dea --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/sender/AbstractSender.java @@ -0,0 +1,116 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.common.sender; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import net.momirealms.customfishing.common.util.Tristate; + +import java.util.UUID; + +/** + * Simple implementation of {@link Sender} using a {@link SenderFactory} + * + * @param the command sender type + */ +public final class AbstractSender implements Sender { + private final CustomFishingPlugin plugin; + private final SenderFactory factory; + private final T sender; + + private final UUID uniqueId; + private final String name; + private final boolean isConsole; + + AbstractSender(CustomFishingPlugin plugin, SenderFactory factory, T sender) { + this.plugin = plugin; + this.factory = factory; + this.sender = sender; + this.uniqueId = factory.getUniqueId(this.sender); + this.name = factory.getName(this.sender); + this.isConsole = this.factory.isConsole(this.sender); + } + + @Override + public CustomFishingPlugin getPlugin() { + return this.plugin; + } + + @Override + public UUID getUniqueId() { + return this.uniqueId; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void sendMessage(Component message) { + this.factory.sendMessage(this.sender, message); + } + + @Override + public void sendMessage(Component message, boolean ignoreEmpty) { + if (ignoreEmpty && message.equals(Component.empty())) { + return; + } + sendMessage(message); + } + + @Override + public Tristate getPermissionValue(String permission) { + return (isConsole() && this.factory.consoleHasAllPermissions()) ? Tristate.TRUE : this.factory.getPermissionValue(this.sender, permission); + } + + @Override + public boolean hasPermission(String permission) { + return (isConsole() && this.factory.consoleHasAllPermissions()) || this.factory.hasPermission(this.sender, permission); + } + + @Override + public void performCommand(String commandLine) { + this.factory.performCommand(this.sender, commandLine); + } + + @Override + public boolean isConsole() { + return this.isConsole; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof AbstractSender that)) return false; + return this.getUniqueId().equals(that.getUniqueId()); + } + + @Override + public int hashCode() { + return this.uniqueId.hashCode(); + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/sender/DummyConsoleSender.java b/common/src/main/java/net/momirealms/customfishing/common/sender/DummyConsoleSender.java new file mode 100644 index 00000000..381116b8 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/sender/DummyConsoleSender.java @@ -0,0 +1,62 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.common.sender; + +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; + +import java.util.UUID; + +public abstract class DummyConsoleSender implements Sender { + private final CustomFishingPlugin platform; + + public DummyConsoleSender(CustomFishingPlugin plugin) { + this.platform = plugin; + } + + @Override + public void performCommand(String commandLine) { + } + + @Override + public boolean isConsole() { + return true; + } + + @Override + public CustomFishingPlugin getPlugin() { + return this.platform; + } + + @Override + public UUID getUniqueId() { + return Sender.CONSOLE_UUID; + } + + @Override + public String getName() { + return Sender.CONSOLE_NAME; + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/sender/Sender.java b/common/src/main/java/net/momirealms/customfishing/common/sender/Sender.java new file mode 100644 index 00000000..2805299c --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/sender/Sender.java @@ -0,0 +1,121 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.common.sender; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import net.momirealms.customfishing.common.util.Tristate; + +import java.util.UUID; + +/** + * Wrapper interface to represent a CommandSender/CommandSource within the common command implementations. + */ +public interface Sender { + + /** The uuid used by the console sender. */ + UUID CONSOLE_UUID = new UUID(0, 0); // 00000000-0000-0000-0000-000000000000 + /** The name used by the console sender. */ + String CONSOLE_NAME = "Console"; + + /** + * Gets the plugin instance the sender is from. + * + * @return the plugin + */ + CustomFishingPlugin getPlugin(); + + /** + * Gets the sender's username + * + * @return a friendly username for the sender + */ + String getName(); + + /** + * Gets the sender's unique id. + * + *

See {@link #CONSOLE_UUID} for the console's UUID representation.

+ * + * @return the sender's uuid + */ + UUID getUniqueId(); + + /** + * Send a json message to the Sender. + * + * @param message the message to send. + */ + void sendMessage(Component message); + + /** + * Send a json message to the Sender. + * + * @param message the message to send. + * @param ignoreEmpty whether to ignore empty component + */ + void sendMessage(Component message, boolean ignoreEmpty); + + /** + * Gets the tristate a permission is set to. + * + * @param permission the permission to check for + * @return a tristate + */ + Tristate getPermissionValue(String permission); + + /** + * Check if the Sender has a permission. + * + * @param permission the permission to check for + * @return true if the sender has the permission + */ + boolean hasPermission(String permission); + + /** + * Makes the sender perform a command. + * + * @param commandLine the command + */ + void performCommand(String commandLine); + + /** + * Gets whether this sender is the console + * + * @return if the sender is the console + */ + boolean isConsole(); + + /** + * Gets whether this sender is still valid & receiving messages. + * + * @return if this sender is valid + */ + default boolean isValid() { + return true; + } + +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/sender/SenderFactory.java b/common/src/main/java/net/momirealms/customfishing/common/sender/SenderFactory.java new file mode 100644 index 00000000..c9aef1db --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/sender/SenderFactory.java @@ -0,0 +1,82 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.common.sender; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import net.momirealms.customfishing.common.util.Tristate; + +import java.util.Objects; +import java.util.UUID; + +/** + * Factory class to make a thread-safe sender instance + * + * @param

the plugin type + * @param the command sender type + */ +public abstract class SenderFactory

implements AutoCloseable { + private final P plugin; + + public SenderFactory(P plugin) { + this.plugin = plugin; + } + + protected P getPlugin() { + return this.plugin; + } + + protected abstract UUID getUniqueId(T sender); + + protected abstract String getName(T sender); + + public abstract Audience getAudience(T sender); + + protected abstract void sendMessage(T sender, Component message); + + protected abstract Tristate getPermissionValue(T sender, String node); + + protected abstract boolean hasPermission(T sender, String node); + + protected abstract void performCommand(T sender, String command); + + protected abstract boolean isConsole(T sender); + + protected boolean consoleHasAllPermissions() { + return true; + } + + public final Sender wrap(T sender) { + Objects.requireNonNull(sender, "sender"); + return new AbstractSender<>(this.plugin, this, sender); + } + + @Override + public void close() { + + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/util/ArrayUtils.java b/common/src/main/java/net/momirealms/customfishing/common/util/ArrayUtils.java new file mode 100644 index 00000000..71b1221d --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/util/ArrayUtils.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.util; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ArrayUtils { + + private ArrayUtils() {} + + public static T[] subArray(T[] array, int index) { + if (index < 0) { + throw new IllegalArgumentException("Index should be a value no lower than 0"); + } + if (array.length <= index) { + @SuppressWarnings("unchecked") + T[] emptyArray = (T[]) Array.newInstance(array.getClass().getComponentType(), 0); + return emptyArray; + } + @SuppressWarnings("unchecked") + T[] subArray = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length - index); + System.arraycopy(array, index, subArray, 0, array.length - index); + return subArray; + } + + public static List splitArray(T[] array, int chunkSize) { + List result = new ArrayList<>(); + for (int i = 0; i < array.length; i += chunkSize) { + int end = Math.min(array.length, i + chunkSize); + @SuppressWarnings("unchecked") + T[] chunk = (T[]) Array.newInstance(array.getClass().getComponentType(), end - i); + System.arraycopy(array, i, chunk, 0, end - i); + result.add(chunk); + } + return result; + } + + public static T[] appendElementToArray(T[] array, T element) { + T[] newArray = Arrays.copyOf(array, array.length + 1); + newArray[array.length] = element; + return newArray; + } + + public static String[] splitValue(String value) { + return value.substring(value.indexOf('[') + 1, value.lastIndexOf(']')) + .replaceAll("\\s", "") + .split(","); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java b/common/src/main/java/net/momirealms/customfishing/common/util/ClassUtils.java similarity index 98% rename from plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java rename to common/src/main/java/net/momirealms/customfishing/common/util/ClassUtils.java index 1de55c96..9330a5ff 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java +++ b/common/src/main/java/net/momirealms/customfishing/common/util/ClassUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.util; +package net.momirealms.customfishing.common.util; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/CompletableFutures.java b/common/src/main/java/net/momirealms/customfishing/common/util/CompletableFutures.java similarity index 98% rename from plugin/src/main/java/net/momirealms/customfishing/util/CompletableFutures.java rename to common/src/main/java/net/momirealms/customfishing/common/util/CompletableFutures.java index 7e9dc660..882eaa69 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/CompletableFutures.java +++ b/common/src/main/java/net/momirealms/customfishing/common/util/CompletableFutures.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.util; +package net.momirealms.customfishing.common.util; import com.google.common.collect.ImmutableList; diff --git a/common/src/main/java/net/momirealms/customfishing/common/util/Either.java b/common/src/main/java/net/momirealms/customfishing/common/util/Either.java new file mode 100644 index 00000000..3464b28d --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/util/Either.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; + +public interface Either { + + static @NotNull Either ofPrimary(final @NotNull U value) { + return EitherImpl.of(requireNonNull(value, "value"), null); + } + + static @NotNull Either ofFallback(final @NotNull V value) { + return EitherImpl.of(null, requireNonNull(value, "value")); + } + + @NotNull + Optional primary(); + + @NotNull + Optional fallback(); + + default @Nullable U primaryOrMapFallback(final @NotNull Function mapFallback) { + return this.primary().orElseGet(() -> mapFallback.apply(this.fallback().get())); + } + + default @Nullable V fallbackOrMapPrimary(final @NotNull Function mapPrimary) { + return this.fallback().orElseGet(() -> mapPrimary.apply(this.primary().get())); + } + + default @NotNull R mapEither( + final @NotNull Function mapPrimary, + final @NotNull Function mapFallback + ) { + return this.primary() + .map(mapPrimary) + .orElseGet(() -> this.fallback().map(mapFallback).get()); + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/util/EitherImpl.java b/common/src/main/java/net/momirealms/customfishing/common/util/EitherImpl.java new file mode 100644 index 00000000..ab6ff54b --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/util/EitherImpl.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.Optional; + +final class EitherImpl implements Either { + private final @Nullable U primary; + private final @Nullable V fallback; + + private EitherImpl(Optional primary, Optional fallback) { + this.primary = primary.orElse(null); + this.fallback = fallback.orElse(null); + } + + private EitherImpl(@Nullable U primary, @Nullable V fallback) { + this.primary = primary; + this.fallback = fallback; + } + + private EitherImpl( + EitherImpl original, + @Nullable U primary, + @Nullable V fallback + ) { + this.primary = primary; + this.fallback = fallback; + } + + @Override + public @NotNull Optional primary() { + return Optional.ofNullable(primary); + } + + @Override + public @NotNull Optional fallback() { + return Optional.ofNullable(fallback); + } + + public final EitherImpl withPrimary(@Nullable U value) { + @Nullable U newValue = value; + if (this.primary == newValue) return this; + return new EitherImpl<>(this, newValue, this.fallback); + } + + public EitherImpl withPrimary(Optional optional) { + @Nullable U value = optional.orElse(null); + if (this.primary == value) return this; + return new EitherImpl<>(this, value, this.fallback); + } + + public EitherImpl withFallback(@Nullable V value) { + @Nullable V newValue = value; + if (this.fallback == newValue) return this; + return new EitherImpl<>(this, this.primary, newValue); + } + + public EitherImpl withFallback(Optional optional) { + @Nullable V value = optional.orElse(null); + if (this.fallback == value) return this; + return new EitherImpl<>(this, this.primary, value); + } + + @Override + public boolean equals(@Nullable Object another) { + if (this == another) return true; + return another instanceof EitherImpl + && equalTo((EitherImpl) another); + } + + private boolean equalTo(EitherImpl another) { + return Objects.equals(primary, another.primary) + && Objects.equals(fallback, another.fallback); + } + + @Override + public int hashCode() { + int h = 5381; + h += (h << 5) + Objects.hashCode(primary); + h += (h << 5) + Objects.hashCode(fallback); + return h; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("Either{"); + if (primary != null) { + builder.append("primary=").append(primary); + } + if (fallback != null) { + if (builder.length() > 7) builder.append(", "); + builder.append("fallback=").append(fallback); + } + return builder.append("}").toString(); + } + + public static EitherImpl of(Optional primary, Optional fallback) { + return new EitherImpl<>(primary, fallback); + } + + public static EitherImpl of(@Nullable U primary, @Nullable V fallback) { + return new EitherImpl<>(primary, fallback); + } + + public static EitherImpl copyOf(Either instance) { + if (instance instanceof EitherImpl) { + return (EitherImpl) instance; + } + return EitherImpl.of(instance.primary(), instance.fallback()); + } +} diff --git a/common/src/main/java/net/momirealms/customfishing/common/util/FileUtils.java b/common/src/main/java/net/momirealms/customfishing/common/util/FileUtils.java new file mode 100644 index 00000000..0cd34962 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/util/FileUtils.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.util; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class FileUtils { + + private FileUtils() {} + + public static Path createFileIfNotExists(Path path) throws IOException { + if (!Files.exists(path)) { + Files.createFile(path); + } + return path; + } + + public static Path createDirectoryIfNotExists(Path path) throws IOException { + if (Files.exists(path) && (Files.isDirectory(path) || Files.isSymbolicLink(path))) { + return path; + } + + try { + Files.createDirectory(path); + } catch (FileAlreadyExistsException e) { + // ignore + } + + return path; + } + + public static Path createDirectoriesIfNotExists(Path path) throws IOException { + if (Files.exists(path) && (Files.isDirectory(path) || Files.isSymbolicLink(path))) { + return path; + } + + try { + Files.createDirectories(path); + } catch (FileAlreadyExistsException e) { + // ignore + } + + return path; + } + + public static void deleteDirectory(Path path) throws IOException { + if (!Files.exists(path) || !Files.isDirectory(path)) { + return; + } + + try (DirectoryStream contents = Files.newDirectoryStream(path)) { + for (Path file : contents) { + if (Files.isDirectory(file)) { + deleteDirectory(file); + } else { + Files.delete(file); + } + } + } + + Files.delete(path); + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/common/Key.java b/common/src/main/java/net/momirealms/customfishing/common/util/Key.java similarity index 84% rename from api/src/main/java/net/momirealms/customfishing/api/common/Key.java rename to common/src/main/java/net/momirealms/customfishing/common/util/Key.java index a276f10c..46037c6b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/common/Key.java +++ b/common/src/main/java/net/momirealms/customfishing/common/util/Key.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.common; +package net.momirealms.customfishing.common.util; public record Key(String namespace, String value) { @@ -23,11 +23,14 @@ public record Key(String namespace, String value) { return new Key(namespace, value); } + public static Key fromString(String key) { + String[] split = key.split(":", 2); + return of(split[0], split[1]); + } + @Override public int hashCode() { - int result = this.namespace.hashCode(); - result = (31 * result) + this.value.hashCode(); - return result; + return toString().hashCode(); } @Override @@ -43,4 +46,4 @@ public record Key(String namespace, String value) { public String toString() { return namespace + ":" + value; } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/data/EarningData.java b/common/src/main/java/net/momirealms/customfishing/common/util/ListUtils.java similarity index 59% rename from api/src/main/java/net/momirealms/customfishing/api/data/EarningData.java rename to common/src/main/java/net/momirealms/customfishing/common/util/ListUtils.java index 38b4d3d6..5550bd6a 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/data/EarningData.java +++ b/common/src/main/java/net/momirealms/customfishing/common/util/ListUtils.java @@ -15,23 +15,22 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.data; +package net.momirealms.customfishing.common.util; -import com.google.gson.annotations.SerializedName; +import java.util.List; -public class EarningData { +public class ListUtils { - @SerializedName("earnings") - public double earnings; - @SerializedName("date") - public int date; - - public EarningData(double earnings, int date) { - this.earnings = earnings; - this.date = date; + private ListUtils() { } - public static EarningData empty() { - return new EarningData(0d, 0); + @SuppressWarnings("unchecked") + public static List toList(final Object obj) { + if (obj instanceof String s) { + return List.of(s); + } else if (obj instanceof List list) { + return (List) list; + } + throw new IllegalArgumentException("Cannot convert " + obj + " to a list"); } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/common/Pair.java b/common/src/main/java/net/momirealms/customfishing/common/util/Pair.java similarity index 94% rename from api/src/main/java/net/momirealms/customfishing/api/common/Pair.java rename to common/src/main/java/net/momirealms/customfishing/common/util/Pair.java index 2491adcd..d665bb11 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/common/Pair.java +++ b/common/src/main/java/net/momirealms/customfishing/common/util/Pair.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.common; +package net.momirealms.customfishing.common.util; public record Pair(L left, R right) { diff --git a/common/src/main/java/net/momirealms/customfishing/common/util/RandomUtils.java b/common/src/main/java/net/momirealms/customfishing/common/util/RandomUtils.java new file mode 100644 index 00000000..760ced64 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/util/RandomUtils.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.util; + +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +public class RandomUtils { + + private final Random random; + + private RandomUtils() { + random = ThreadLocalRandom.current(); + } + + private static class SingletonHolder { + private static final RandomUtils INSTANCE = new RandomUtils(); + } + + private static RandomUtils getInstance() { + return SingletonHolder.INSTANCE; + } + + public static int generateRandomInt(int min, int max) { + return getInstance().random.nextInt(max - min + 1) + min; + } + + public static double generateRandomDouble(double min, double max) { + return min + (max - min) * getInstance().random.nextDouble(); + } + + public static float generateRandomFloat(float min, float max) { + return min + (max - min) * getInstance().random.nextFloat(); + } + + public static boolean generateRandomBoolean() { + return getInstance().random.nextBoolean(); + } + + public static T getRandomElementFromArray(T[] array) { + int index = getInstance().random.nextInt(array.length); + return array[index]; + } + + public static double triangle(double mode, double deviation) { + return mode + deviation * (generateRandomDouble(0,1) - generateRandomDouble(0,1)); + } + + public static T[] getRandomElementsFromArray(T[] array, int count) { + if (count > array.length) { + throw new IllegalArgumentException("Count cannot be greater than array length"); + } + + @SuppressWarnings("unchecked") + T[] result = (T[]) new Object[count]; + + for (int i = 0; i < count; i++) { + int index = getInstance().random.nextInt(array.length - i); + result[i] = array[index]; + array[index] = array[array.length - i - 1]; + } + + return result; + } +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/CommandManager.java b/common/src/main/java/net/momirealms/customfishing/common/util/TriConsumer.java similarity index 84% rename from api/src/main/java/net/momirealms/customfishing/api/manager/CommandManager.java rename to common/src/main/java/net/momirealms/customfishing/common/util/TriConsumer.java index 975ec5e4..470cb71b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/CommandManager.java +++ b/common/src/main/java/net/momirealms/customfishing/common/util/TriConsumer.java @@ -15,11 +15,8 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.manager; +package net.momirealms.customfishing.common.util; -public interface CommandManager { - - void load(); - - void unload(); -} +public interface TriConsumer { + void accept(K k, V v, S s); +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customfishing/common/util/TriFunction.java b/common/src/main/java/net/momirealms/customfishing/common/util/TriFunction.java new file mode 100644 index 00000000..7f038164 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/util/TriFunction.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.util; + +import java.util.Objects; +import java.util.function.Function; + +public interface TriFunction { + R apply(T var1, U var2, V var3); + + default TriFunction andThen(Function after) { + Objects.requireNonNull(after); + return (t, u, v) -> { + return after.apply(this.apply(t, u, v)); + }; + } +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customfishing/common/util/Tristate.java b/common/src/main/java/net/momirealms/customfishing/common/util/Tristate.java new file mode 100644 index 00000000..e3e5f1a5 --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/util/Tristate.java @@ -0,0 +1,98 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.common.util; + +import org.jetbrains.annotations.NotNull; + +/** + * Represents three different states of a setting. + * + *

Possible values:

+ *

+ *
    + *
  • {@link #TRUE} - a positive setting
  • + *
  • {@link #FALSE} - a negative (negated) setting
  • + *
  • {@link #UNDEFINED} - a non-existent setting
  • + *
+ */ +public enum Tristate { + + /** + * A value indicating a positive setting + */ + TRUE(true), + + /** + * A value indicating a negative (negated) setting + */ + FALSE(false), + + /** + * A value indicating a non-existent setting + */ + UNDEFINED(false); + + /** + * Returns a {@link Tristate} from a boolean + * + * @param val the boolean value + * @return {@link #TRUE} or {@link #FALSE}, if the value is true or false, respectively. + */ + public static @NotNull Tristate of(boolean val) { + return val ? TRUE : FALSE; + } + + /** + * Returns a {@link Tristate} from a nullable boolean. + * + *

Unlike {@link #of(boolean)}, this method returns {@link #UNDEFINED} + * if the value is null.

+ * + * @param val the boolean value + * @return {@link #UNDEFINED}, {@link #TRUE} or {@link #FALSE}, if the value + * is null, true or false, respectively. + */ + public static @NotNull Tristate of(Boolean val) { + return val == null ? UNDEFINED : val ? TRUE : FALSE; + } + + private final boolean booleanValue; + + Tristate(boolean booleanValue) { + this.booleanValue = booleanValue; + } + + /** + * Returns the value of the Tristate as a boolean. + * + *

A value of {@link #UNDEFINED} converts to false.

+ * + * @return a boolean representation of the Tristate. + */ + public boolean asBoolean() { + return this.booleanValue; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/NumberUtils.java b/common/src/main/java/net/momirealms/customfishing/common/util/Tuple.java similarity index 74% rename from plugin/src/main/java/net/momirealms/customfishing/util/NumberUtils.java rename to common/src/main/java/net/momirealms/customfishing/common/util/Tuple.java index d2f053ec..94ad7fe7 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/NumberUtils.java +++ b/common/src/main/java/net/momirealms/customfishing/common/util/Tuple.java @@ -15,12 +15,11 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.util; +package net.momirealms.customfishing.common.util; -public class NumberUtils { +public record Tuple(L left, M mid, R right) { - public static String money(double money) { - String str = String.format("%.2f", money); - return str.replace(",", "."); + public static Tuple of(final L left, final M mid, final R right) { + return new Tuple<>(left, mid, right); } -} +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customfishing/common/util/UUIDUtils.java b/common/src/main/java/net/momirealms/customfishing/common/util/UUIDUtils.java new file mode 100644 index 00000000..3b6d7c5e --- /dev/null +++ b/common/src/main/java/net/momirealms/customfishing/common/util/UUIDUtils.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.common.util; + +import java.math.BigInteger; +import java.util.UUID; + +public class UUIDUtils { + + public static UUID fromUnDashedUUID(String id) { + return id == null ? null : new UUID( + new BigInteger(id.substring(0, 16), 16).longValue(), + new BigInteger(id.substring(16, 32), 16).longValue() + ); + } + + public static String toUnDashedUUID(UUID uuid) { + return uuid.toString().replace("-", ""); + } + + public static UUID uuidFromIntArray(int[] array) { + return new UUID((long)array[0] << 32 | (long)array[1] & 4294967295L, (long)array[2] << 32 | (long)array[3] & 4294967295L); + } + + public static int[] uuidToIntArray(UUID uuid) { + long l = uuid.getMostSignificantBits(); + long m = uuid.getLeastSignificantBits(); + return leastMostToIntArray(l, m); + } + + private static int[] leastMostToIntArray(long uuidMost, long uuidLeast) { + return new int[]{(int)(uuidMost >> 32), (int)uuidMost, (int)(uuidLeast >> 32), (int)uuidLeast}; + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/util/WeightUtils.java b/common/src/main/java/net/momirealms/customfishing/common/util/WeightUtils.java similarity index 96% rename from api/src/main/java/net/momirealms/customfishing/api/util/WeightUtils.java rename to common/src/main/java/net/momirealms/customfishing/common/util/WeightUtils.java index 4de91cab..68e770dd 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/util/WeightUtils.java +++ b/common/src/main/java/net/momirealms/customfishing/common/util/WeightUtils.java @@ -15,9 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.api.util; - -import net.momirealms.customfishing.api.common.Pair; +package net.momirealms.customfishing.common.util; import java.util.ArrayList; import java.util.Arrays; @@ -27,8 +25,11 @@ import java.util.Map; /** * Utility class for selecting random items based on weights. */ +@SuppressWarnings("DuplicatedCode") public class WeightUtils { + private WeightUtils() {} + /** * Get a random item from a list of pairs, each associated with a weight. * @@ -81,6 +82,7 @@ public class WeightUtils { * @return A randomly selected item from the list, or null if no item was selected. */ private static T getRandom(double[] weights, List available, int effectiveSize) { + if (available.isEmpty()) return null; double total = Arrays.stream(weights).sum(); double[] weightRatios = new double[effectiveSize]; for (int i = 0; i < effectiveSize; i++){ diff --git a/common/src/main/resources/library-version.properties b/common/src/main/resources/library-version.properties new file mode 100644 index 00000000..1e8ea705 --- /dev/null +++ b/common/src/main/resources/library-version.properties @@ -0,0 +1,27 @@ +config=${config_version} +asm=${asm_version} +asm-commons=${asm_commons_version} +jar-relocator=${jar_relocator_version} +h2-driver=${h2_driver_version} +sqlite-driver=${sqlite_driver_version} +cloud-core=${cloud_core_version} +cloud-brigadier=${cloud_brigadier_version} +cloud-services=${cloud_services_version} +cloud-bukkit=${cloud_bukkit_version} +cloud-paper=${cloud_paper_version} +cloud-minecraft-extras=${cloud_minecraft_extras_version} +boosted-yaml=${boosted_yaml_version} +byte-buddy=${byte_buddy_version} +mongodb-driver-core=${mongodb_driver_version} +mariadb-java-client=${mariadb_driver_version} +mysql-connector-j=${mysql_driver_version} +hikari-cp=${hikari_version} +commons-pool=${commons_pool_version} +bstats-base=${bstats_version} +geantyref=${geantyref_version} +gson=${gson_version} +caffeine=${caffeine_version} +jedis=${jedis_version} +exp4j=${exp4j_version} +slf4j=${slf4j_version} +lz4-java=${lz4_version} \ No newline at end of file diff --git a/compatibility/build.gradle.kts b/compatibility/build.gradle.kts new file mode 100644 index 00000000..bcb26bcc --- /dev/null +++ b/compatibility/build.gradle.kts @@ -0,0 +1,75 @@ +repositories { + maven("https://jitpack.io/") // itemsadder + maven("https://mvn.lumine.io/repository/maven-public/") // mythicmobs + maven("https://nexus.phoenixdevt.fr/repository/maven-public/") // mmoitems + maven("https://papermc.io/repo/repository/maven-public/") + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // papi + maven("https://r.irepo.space/maven/") // neigeitems + maven("https://repo.oraxen.com/releases/") // oraxen + maven("https://repo.auxilor.io/repository/maven-public/") // eco + maven("https://nexus.betonquest.org/repository/betonquest/") // betonquest + maven("https://repo.dmulloy2.net/repository/public/") // betonquest needs packet wrapper? + maven("https://maven.enginehub.org/repo/") // worldguard +} + +dependencies { + compileOnly(project(":common")) + compileOnly(project(":api")) + compileOnly("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}") { + exclude(module = "adventure-bom") + exclude(module = "checker-qual") + exclude(module = "annotations") + } + compileOnly("org.jetbrains:annotations:${rootProject.properties["jetbrains_annotations_version"]}") + // papi + compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}") + // server + compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT") + // vault + compileOnly("com.github.MilkBowl:VaultAPI:${rootProject.properties["vault_version"]}") + // season + compileOnly("com.github.Xiao-MoMi:Custom-Crops:3.4.8") + compileOnly(files("libs/RealisticSeasons-api.jar")) + compileOnly(files("libs/AdvancedSeasons-API.jar")) + // enchantment + compileOnly(files("libs/AdvancedEnchantments-api.jar")) + // leveler + compileOnly(files("libs/mcMMO-api.jar")) + compileOnly("net.Indyuce:MMOCore-API:1.12.1-SNAPSHOT") + compileOnly("dev.aurelium:auraskills-api-bukkit:2.0.7") + compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.21") + compileOnly("com.github.Zrips:Jobs:4.17.2") + // quest + compileOnly(files("libs/BattlePass-4.0.6-api.jar")) + compileOnly(files("libs/ClueScrolls-4.8.7-api.jar")) + compileOnly(files("libs/notquests-5.17.1.jar")) + compileOnly("org.betonquest:betonquest:2.0.1") + // item + compileOnly(files("libs/zaphkiel-2.0.24.jar")) + compileOnly("com.github.LoneDev6:API-ItemsAdder:3.6.1") + compileOnly("net.Indyuce:MMOItems-API:6.10-SNAPSHOT") + compileOnly("io.lumine:MythicLib-dist:1.6.2-SNAPSHOT") + compileOnly("pers.neige.neigeitems:NeigeItems:1.17.13") + compileOnly("io.th0rgal:oraxen:1.168.0") + // entity + compileOnly("io.lumine:Mythic-Dist:5.6.2") + // eco + compileOnly("com.willfp:eco:6.70.1") + compileOnly("com.willfp:EcoJobs:3.56.1") + compileOnly("com.willfp:EcoSkills:3.46.1") + compileOnly("com.willfp:libreforge:4.58.1") +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) +} \ No newline at end of file diff --git a/plugin/libs/AdvancedEnchantments-api.jar b/compatibility/libs/AdvancedEnchantments-api.jar similarity index 100% rename from plugin/libs/AdvancedEnchantments-api.jar rename to compatibility/libs/AdvancedEnchantments-api.jar diff --git a/compatibility/libs/AdvancedSeasons-API.jar b/compatibility/libs/AdvancedSeasons-API.jar new file mode 100644 index 00000000..19ddf212 Binary files /dev/null and b/compatibility/libs/AdvancedSeasons-API.jar differ diff --git a/plugin/libs/BattlePass-4.0.6-api.jar b/compatibility/libs/BattlePass-4.0.6-api.jar similarity index 100% rename from plugin/libs/BattlePass-4.0.6-api.jar rename to compatibility/libs/BattlePass-4.0.6-api.jar diff --git a/plugin/libs/ClueScrolls-4.8.7-api.jar b/compatibility/libs/ClueScrolls-4.8.7-api.jar similarity index 100% rename from plugin/libs/ClueScrolls-4.8.7-api.jar rename to compatibility/libs/ClueScrolls-4.8.7-api.jar diff --git a/plugin/libs/RealisticSeasons-api.jar b/compatibility/libs/RealisticSeasons-api.jar similarity index 100% rename from plugin/libs/RealisticSeasons-api.jar rename to compatibility/libs/RealisticSeasons-api.jar diff --git a/plugin/libs/mcMMO-api.jar b/compatibility/libs/mcMMO-api.jar similarity index 100% rename from plugin/libs/mcMMO-api.jar rename to compatibility/libs/mcMMO-api.jar diff --git a/plugin/libs/notquests-5.17.1.jar b/compatibility/libs/notquests-5.17.1.jar similarity index 100% rename from plugin/libs/notquests-5.17.1.jar rename to compatibility/libs/notquests-5.17.1.jar diff --git a/plugin/libs/zaphkiel-2.0.24.jar b/compatibility/libs/zaphkiel-2.0.24.jar similarity index 100% rename from plugin/libs/zaphkiel-2.0.24.jar rename to compatibility/libs/zaphkiel-2.0.24.jar diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/VaultHook.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/VaultHook.java similarity index 65% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/VaultHook.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/VaultHook.java index 14c2e391..a186bd92 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/VaultHook.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/VaultHook.java @@ -15,10 +15,11 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility; +package net.momirealms.customfishing.bukkit.integration; import net.milkbowl.vault.economy.Economy; -import net.momirealms.customfishing.api.CustomFishingPlugin; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; import org.bukkit.plugin.RegisteredServiceProvider; public class VaultHook { @@ -26,7 +27,7 @@ public class VaultHook { private static Economy economy; public static boolean initialize() { - RegisteredServiceProvider rsp = CustomFishingPlugin.getInstance().getServer().getServicesManager().getRegistration(Economy.class); + RegisteredServiceProvider rsp = Bukkit.getServicesManager().getRegistration(Economy.class); if (rsp == null) { return false; } @@ -37,4 +38,16 @@ public class VaultHook { public static Economy getEconomy() { return economy; } + + public static void deposit(OfflinePlayer player, double amount) { + economy.depositPlayer(player, amount); + } + + public static void withdraw(OfflinePlayer player, double amount) { + economy.withdrawPlayer(player, amount); + } + + public static double getBalance(OfflinePlayer player) { + return economy.getBalance(player); + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/block/ItemsAdderBlockImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/block/ItemsAdderBlockProvider.java similarity index 70% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/block/ItemsAdderBlockImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/block/ItemsAdderBlockProvider.java index 43da1177..14b4de98 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/block/ItemsAdderBlockImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/block/ItemsAdderBlockProvider.java @@ -15,35 +15,37 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.block; +package net.momirealms.customfishing.bukkit.integration.block; import dev.lone.itemsadder.api.CustomBlock; +import net.momirealms.customfishing.api.integration.BlockProvider; import net.momirealms.customfishing.api.mechanic.block.BlockDataModifier; -import net.momirealms.customfishing.api.mechanic.block.BlockLibrary; +import net.momirealms.customfishing.api.mechanic.context.Context; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import java.util.List; -public class ItemsAdderBlockImpl implements BlockLibrary { +public class ItemsAdderBlockProvider implements BlockProvider { @Override - public String identification() { + public String identifier() { return "ItemsAdder"; } @Override - public BlockData getBlockData(Player player, String id, List modifiers) { + public BlockData blockData(@NotNull Context context, @NotNull String id, List modifiers) { BlockData blockData = CustomBlock.getBaseBlockData(id); for (BlockDataModifier modifier : modifiers) { - modifier.apply(player, blockData); + modifier.apply(context, blockData); } return blockData; } @Override - public String getBlockID(Block block) { + public String blockID(@NotNull Block block) { CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); return customBlock == null ? null : customBlock.getId(); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/block/VanillaBlockImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/block/OraxenBlockProvider.java similarity index 54% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/block/VanillaBlockImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/block/OraxenBlockProvider.java index 5709759e..2f5701f2 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/block/VanillaBlockImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/block/OraxenBlockProvider.java @@ -15,37 +15,36 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.block; +package net.momirealms.customfishing.bukkit.integration.block; +import io.th0rgal.oraxen.api.OraxenBlocks; +import io.th0rgal.oraxen.mechanics.Mechanic; +import net.momirealms.customfishing.api.integration.BlockProvider; import net.momirealms.customfishing.api.mechanic.block.BlockDataModifier; -import net.momirealms.customfishing.api.mechanic.block.BlockLibrary; -import org.bukkit.Material; +import net.momirealms.customfishing.api.mechanic.context.Context; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.List; -import java.util.Locale; -public class VanillaBlockImpl implements BlockLibrary { +public class OraxenBlockProvider implements BlockProvider { @Override - public String identification() { - return "vanilla"; + public String identifier() { + return "Oraxen"; } @Override - public BlockData getBlockData(Player player, String id, List modifiers) { - BlockData blockData = Material.valueOf(id.toUpperCase(Locale.ENGLISH)).createBlockData(); - for (BlockDataModifier modifier : modifiers) { - modifier.apply(player, blockData); - } - return blockData; + public BlockData blockData(@NotNull Context context, @NotNull String id, List modifiers) { + return null; } @Override - public @Nullable String getBlockID(Block block) { - return block.getType().name(); + public String blockID(@NotNull Block block) { + Mechanic mechanic = OraxenBlocks.getOraxenBlock(block.getBlockData()); + if (mechanic == null) return null; + return mechanic.getItemID(); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/enchant/AdvancedEnchantmentsImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/enchant/AdvancedEnchantmentsProvider.java similarity index 61% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/enchant/AdvancedEnchantmentsImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/enchant/AdvancedEnchantmentsProvider.java index fd5cd1cc..db8c0ab8 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/enchant/AdvancedEnchantmentsImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/enchant/AdvancedEnchantmentsProvider.java @@ -15,23 +15,30 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.enchant; +package net.momirealms.customfishing.bukkit.integration.enchant; import net.advancedplugins.ae.api.AEAPI; -import net.momirealms.customfishing.api.integration.EnchantmentInterface; +import net.momirealms.customfishing.api.integration.EnchantmentProvider; +import net.momirealms.customfishing.common.util.Pair; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; import java.util.Map; -public class AdvancedEnchantmentsImpl implements EnchantmentInterface { +public class AdvancedEnchantmentsProvider implements EnchantmentProvider { @Override - public List getEnchants(ItemStack itemStack) { - List enchants = new ArrayList<>(); + public String identifier() { + return "AdvancedEnchantments"; + } + + @Override + public List> getEnchants(@NotNull ItemStack itemStack) { + List> enchants = new ArrayList<>(); for (Map.Entry entry : AEAPI.getEnchantmentsOnItem(itemStack).entrySet()) { - enchants.add("AE:" + entry.getKey() + ":" + entry.getValue()); + enchants.add(Pair.of("AE:" + entry.getKey(), entry.getValue().shortValue())); } return enchants; } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/enchant/VanillaEnchantmentsImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/enchant/VanillaEnchantmentsProvider.java similarity index 62% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/enchant/VanillaEnchantmentsImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/enchant/VanillaEnchantmentsProvider.java index 55ad72c8..a604f1e1 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/enchant/VanillaEnchantmentsImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/enchant/VanillaEnchantmentsProvider.java @@ -15,25 +15,31 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.enchant; +package net.momirealms.customfishing.bukkit.integration.enchant; -import net.momirealms.customfishing.api.integration.EnchantmentInterface; +import net.momirealms.customfishing.api.integration.EnchantmentProvider; +import net.momirealms.customfishing.common.util.Pair; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; import java.util.Map; -public class VanillaEnchantmentsImpl implements EnchantmentInterface { +public class VanillaEnchantmentsProvider implements EnchantmentProvider { @Override - public List getEnchants(ItemStack itemStack) { + public String identifier() { + return "vanilla"; + } + + @Override + public List> getEnchants(@NotNull ItemStack itemStack) { Map enchantments = itemStack.getEnchantments(); - List enchants = new ArrayList<>(enchantments.size()); + List> enchants = new ArrayList<>(enchantments.size()); for (Map.Entry en : enchantments.entrySet()) { - String key = en.getKey().getKey() + ":" + en.getValue(); - enchants.add(key); + enchants.add(Pair.of(en.getKey().getKey().toString(), en.getValue().shortValue())); } return enchants; } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/entity/ItemsAdderEntityImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/entity/ItemsAdderEntityProvider.java similarity index 74% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/entity/ItemsAdderEntityImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/entity/ItemsAdderEntityProvider.java index 09f5223c..2b26586a 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/entity/ItemsAdderEntityImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/entity/ItemsAdderEntityProvider.java @@ -15,24 +15,26 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.entity; +package net.momirealms.customfishing.bukkit.integration.entity; import dev.lone.itemsadder.api.CustomEntity; -import net.momirealms.customfishing.api.mechanic.entity.EntityLibrary; +import net.momirealms.customfishing.api.integration.EntityProvider; import org.bukkit.Location; import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; import java.util.Map; -public class ItemsAdderEntityImpl implements EntityLibrary { +public class ItemsAdderEntityProvider implements EntityProvider { @Override - public String identification() { - return "vanilla"; + public String identifier() { + return "ItemsAdder"; } + @NotNull @Override - public Entity spawn(Location location, String id, Map propertyMap) { + public Entity spawn(@NotNull Location location, @NotNull String id, @NotNull Map propertyMap) { CustomEntity customEntity = CustomEntity.spawn( id, location, diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/entity/MythicEntityImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/entity/MythicEntityProvider.java similarity index 78% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/entity/MythicEntityImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/entity/MythicEntityProvider.java index 464864ae..78824e7c 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/entity/MythicEntityImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/entity/MythicEntityProvider.java @@ -15,36 +15,33 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.entity; +package net.momirealms.customfishing.bukkit.integration.entity; import io.lumine.mythic.api.adapters.AbstractLocation; import io.lumine.mythic.api.mobs.MythicMob; import io.lumine.mythic.bukkit.MythicBukkit; import io.lumine.mythic.bukkit.utils.serialize.Position; import io.lumine.mythic.core.mobs.ActiveMob; -import net.momirealms.customfishing.api.mechanic.entity.EntityLibrary; -import net.momirealms.customfishing.util.ConfigUtils; +import net.momirealms.customfishing.api.integration.EntityProvider; import org.bukkit.Location; import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; import java.util.Map; import java.util.Optional; -public class MythicEntityImpl implements EntityLibrary { +public class MythicEntityProvider implements EntityProvider { private MythicBukkit mythicBukkit; - public MythicEntityImpl() { - this.mythicBukkit = MythicBukkit.inst(); - } - @Override - public String identification() { + public String identifier() { return "MythicMobs"; } + @NotNull @Override - public Entity spawn(Location location, String id, Map propertyMap) { + public Entity spawn(@NotNull Location location, @NotNull String id, @NotNull Map propertyMap) { if (this.mythicBukkit == null || mythicBukkit.isClosed()) { this.mythicBukkit = MythicBukkit.inst(); } @@ -53,7 +50,7 @@ public class MythicEntityImpl implements EntityLibrary { MythicMob theMob = mythicMob.get(); Position position = Position.of(location); AbstractLocation abstractLocation = new AbstractLocation(position); - ActiveMob activeMob = theMob.spawn(abstractLocation, ConfigUtils.getDoubleValue(propertyMap.get("level"))); + ActiveMob activeMob = theMob.spawn(abstractLocation, (double) propertyMap.getOrDefault("level", 0d)); return activeMob.getEntity().getBukkitEntity(); } throw new NullPointerException("MythicMobs: " + id + " doesn't exist."); diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/entity/VanillaEntityImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/entity/VanillaEntityProvider.java similarity index 71% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/entity/VanillaEntityImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/entity/VanillaEntityProvider.java index da5e9139..90b53afb 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/entity/VanillaEntityImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/entity/VanillaEntityProvider.java @@ -15,25 +15,27 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.entity; +package net.momirealms.customfishing.bukkit.integration.entity; -import net.momirealms.customfishing.api.mechanic.entity.EntityLibrary; +import net.momirealms.customfishing.api.integration.EntityProvider; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.jetbrains.annotations.NotNull; import java.util.Locale; import java.util.Map; -public class VanillaEntityImpl implements EntityLibrary { +public class VanillaEntityProvider implements EntityProvider { @Override - public String identification() { + public String identifier() { return "vanilla"; } + @NotNull @Override - public Entity spawn(Location location, String id, Map propertyMap) { + public Entity spawn(@NotNull Location location, @NotNull String id, @NotNull Map propertyMap) { return location.getWorld().spawnEntity(location, EntityType.valueOf(id.toUpperCase(Locale.ENGLISH))); } } diff --git a/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/CustomFishingItemProvider.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/CustomFishingItemProvider.java new file mode 100644 index 00000000..fa51e3ec --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/CustomFishingItemProvider.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.integration.item; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.integration.ItemProvider; +import net.momirealms.customfishing.api.mechanic.context.Context; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import static java.util.Objects.requireNonNull; + +public class CustomFishingItemProvider implements ItemProvider { + + @Override + public String identifier() { + return "CustomFishing"; + } + + @NotNull + @Override + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { + String[] split = id.split(":", 2); + String finalID; + if (split.length == 1) { + // CustomFishing:ID + finalID = split[0]; + } else { + // CustomFishing:TYPE:ID + finalID = split[1]; + } + ItemStack itemStack = BukkitCustomFishingPlugin.getInstance().getItemManager().buildInternal(Context.player(player), finalID); + return requireNonNull(itemStack); + } + + @Override + public String itemID(@NotNull ItemStack itemStack) { + return BukkitCustomFishingPlugin.getInstance().getItemManager().getCustomFishingItemID(itemStack); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/ItemsAdderItemImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/ItemsAdderItemProvider.java similarity index 55% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/item/ItemsAdderItemImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/ItemsAdderItemProvider.java index 773910ea..a8a5f3f3 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/ItemsAdderItemImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/ItemsAdderItemProvider.java @@ -15,35 +15,34 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.item; +package net.momirealms.customfishing.bukkit.integration.item; import dev.lone.itemsadder.api.CustomStack; -import net.momirealms.customfishing.api.mechanic.item.ItemLibrary; -import net.momirealms.customfishing.api.util.LogUtils; +import net.momirealms.customfishing.api.integration.ItemProvider; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; -public class ItemsAdderItemImpl implements ItemLibrary { +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class ItemsAdderItemProvider implements ItemProvider { @Override - public String identification() { + public String identifier() { return "ItemsAdder"; } + @NotNull @Override - public ItemStack buildItem(Player player, String id) { - CustomStack stack = CustomStack.getInstance(id); - if (stack == null) { - LogUtils.severe(id + " doesn't exist in ItemsAdder configs."); - return null; - } + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { + CustomStack stack = requireNonNull(CustomStack.getInstance(id)); return stack.getItemStack(); } @Override - public String getItemID(ItemStack itemStack) { - CustomStack customStack = CustomStack.byItemStack(itemStack); - if (customStack == null) return null; - return customStack.getNamespacedID(); + public String itemID(@NotNull ItemStack itemStack) { + return Optional.ofNullable(CustomStack.byItemStack(itemStack)).map(CustomStack::getNamespacedID).orElse(null); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/MMOItemsItemImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/MMOItemsItemProvider.java similarity index 66% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/item/MMOItemsItemImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/MMOItemsItemProvider.java index 1cf8409a..603fedb1 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/MMOItemsItemImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/MMOItemsItemProvider.java @@ -15,36 +15,40 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.item; +package net.momirealms.customfishing.bukkit.integration.item; -import de.tr7zw.changeme.nbtapi.NBTItem; +import io.lumine.mythic.lib.api.item.NBTItem; import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem; -import net.momirealms.customfishing.api.mechanic.item.ItemLibrary; +import net.momirealms.customfishing.api.integration.ItemProvider; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import java.util.Locale; -public class MMOItemsItemImpl implements ItemLibrary { +import static java.util.Objects.requireNonNull; + +public class MMOItemsItemProvider implements ItemProvider { @Override - public String identification() { + public String identifier() { return "MMOItems"; } + @NotNull @Override - public ItemStack buildItem(Player player, String id) { + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { String[] split = id.split(":"); MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), split[1].toUpperCase(Locale.ENGLISH)); - return mmoItem == null ? new ItemStack(Material.AIR) : mmoItem.newBuilder().build(); + return mmoItem == null ? new ItemStack(Material.AIR) : requireNonNull(mmoItem.newBuilder().build()); } @Override - public String getItemID(ItemStack itemStack) { - NBTItem nbtItem = new NBTItem(itemStack); + public String itemID(@NotNull ItemStack itemStack) { + NBTItem nbtItem = NBTItem.get(itemStack); if (!nbtItem.hasTag("MMOITEMS_ITEM_ID")) return null; return nbtItem.getString("MMOITEMS_ITEM_ID"); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/McMMOTreasureImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/McMMOTreasureProvider.java similarity index 79% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/item/McMMOTreasureImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/McMMOTreasureProvider.java index 6ca323b7..b379e48b 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/McMMOTreasureImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/McMMOTreasureProvider.java @@ -15,24 +15,25 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.item; +package net.momirealms.customfishing.bukkit.integration.item; -import net.momirealms.customfishing.api.mechanic.item.ItemLibrary; +import net.momirealms.customfishing.api.integration.ItemProvider; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -public class McMMOTreasureImpl implements ItemLibrary { +public class McMMOTreasureProvider implements ItemProvider { private final Method getMcMMOPlayerMethod; private final Method getFishingManagerMethod; private final Method getFishingTreasureMethod; private final Method getItemStackMethod; - public McMMOTreasureImpl() throws ClassNotFoundException, NoSuchMethodException { + public McMMOTreasureProvider() throws ClassNotFoundException, NoSuchMethodException { Class userClass = Class.forName("com.gmail.nossr50.util.player.UserManager"); getMcMMOPlayerMethod = userClass.getMethod("getPlayer", Player.class); Class mcMMOPlayerClass = Class.forName("com.gmail.nossr50.datatypes.player.McMMOPlayer"); @@ -45,12 +46,13 @@ public class McMMOTreasureImpl implements ItemLibrary { } @Override - public String identification() { + public String identifier() { return "mcMMO"; } + @NotNull @Override - public ItemStack buildItem(Player player, String id) { + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { if (!id.equals("treasure")) return new ItemStack(Material.AIR); ItemStack itemStack = null; int times = 0; @@ -68,11 +70,11 @@ public class McMMOTreasureImpl implements ItemLibrary { times++; } } - return itemStack == null ? new ItemStack(Material.COD) : itemStack; + return itemStack == null ? (Math.random() > 0.5 ? new ItemStack(Material.COD) : (Math.random() > 0.2) ? new ItemStack(Material.SALMON) : new ItemStack(Material.PUFFERFISH)) : itemStack; } @Override - public String getItemID(ItemStack itemStack) { + public String itemID(@NotNull ItemStack itemStack) { return null; } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/MythicMobsItemImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/MythicMobsItemProvider.java similarity index 64% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/item/MythicMobsItemImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/MythicMobsItemProvider.java index 28a45b51..fd277c2d 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/MythicMobsItemImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/MythicMobsItemProvider.java @@ -15,29 +15,26 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.item; +package net.momirealms.customfishing.bukkit.integration.item; -import de.tr7zw.changeme.nbtapi.NBTItem; import io.lumine.mythic.bukkit.MythicBukkit; -import net.momirealms.customfishing.api.mechanic.item.ItemLibrary; +import net.momirealms.customfishing.api.integration.ItemProvider; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; -public class MythicMobsItemImpl implements ItemLibrary { +public class MythicMobsItemProvider implements ItemProvider { private MythicBukkit mythicBukkit; - public MythicMobsItemImpl() { - this.mythicBukkit = MythicBukkit.inst(); - } - @Override - public String identification() { + public String identifier() { return "MythicMobs"; } + @NotNull @Override - public ItemStack buildItem(Player player, String id) { + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { if (mythicBukkit == null || mythicBukkit.isClosed()) { this.mythicBukkit = MythicBukkit.inst(); } @@ -45,8 +42,10 @@ public class MythicMobsItemImpl implements ItemLibrary { } @Override - public String getItemID(ItemStack itemStack) { - NBTItem nbtItem = new NBTItem(itemStack); - return nbtItem.hasTag("MYTHIC_TYPE") ? nbtItem.getString("MYTHIC_TYPE") : null; + public String itemID(@NotNull ItemStack itemStack) { + if (mythicBukkit == null || mythicBukkit.isClosed()) { + this.mythicBukkit = MythicBukkit.inst(); + } + return mythicBukkit.getItemManager().getMythicTypeFromItem(itemStack); } } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/NeigeItemsItemImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/NeigeItemsItemProvider.java similarity index 68% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/item/NeigeItemsItemImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/NeigeItemsItemProvider.java index 43047613..e6001a06 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/NeigeItemsItemImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/NeigeItemsItemProvider.java @@ -15,29 +15,33 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.item; +package net.momirealms.customfishing.bukkit.integration.item; -import net.momirealms.customfishing.api.mechanic.item.ItemLibrary; +import net.momirealms.customfishing.api.integration.ItemProvider; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import pers.neige.neigeitems.item.ItemInfo; import pers.neige.neigeitems.manager.ItemManager; import pers.neige.neigeitems.utils.ItemUtils; -public class NeigeItemsItemImpl implements ItemLibrary { +import java.util.Objects; + +public class NeigeItemsItemProvider implements ItemProvider { @Override - public String identification() { + public String identifier() { return "NeigeItems"; } + @NotNull @Override - public ItemStack buildItem(Player player, String id) { - return ItemManager.INSTANCE.getItemStack(id, player); + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { + return Objects.requireNonNull(ItemManager.INSTANCE.getItemStack(id, player)); } @Override - public String getItemID(ItemStack itemStack) { + public String itemID(@NotNull ItemStack itemStack) { ItemInfo itemInfo = ItemUtils.isNiItem(itemStack); if (itemInfo != null) { return itemInfo.getId(); diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/OraxenItemImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/OraxenItemProvider.java similarity index 74% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/item/OraxenItemImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/OraxenItemProvider.java index 85dd40b5..a709ac1d 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/OraxenItemImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/OraxenItemProvider.java @@ -15,30 +15,32 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.item; +package net.momirealms.customfishing.bukkit.integration.item; import io.th0rgal.oraxen.api.OraxenItems; import io.th0rgal.oraxen.items.ItemBuilder; -import net.momirealms.customfishing.api.mechanic.item.ItemLibrary; +import net.momirealms.customfishing.api.integration.ItemProvider; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; -public class OraxenItemImpl implements ItemLibrary { +public class OraxenItemProvider implements ItemProvider { @Override - public String identification() { + public String identifier() { return "Oraxen"; } + @NotNull @Override - public ItemStack buildItem(Player player, String id) { + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { ItemBuilder itemBuilder = OraxenItems.getItemById(id); return itemBuilder == null ? new ItemStack(Material.AIR) : itemBuilder.build(); } @Override - public String getItemID(ItemStack itemStack) { + public String itemID(@NotNull ItemStack itemStack) { return OraxenItems.getIdByItem(itemStack); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/ZaphkielItemImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/ZaphkielItemProvider.java similarity index 67% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/item/ZaphkielItemImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/ZaphkielItemProvider.java index d1dc0129..ea2ddb2e 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/ZaphkielItemImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/item/ZaphkielItemProvider.java @@ -15,35 +15,39 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.item; +package net.momirealms.customfishing.bukkit.integration.item; import ink.ptms.zaphkiel.ZapAPI; import ink.ptms.zaphkiel.Zaphkiel; -import net.momirealms.customfishing.api.mechanic.item.ItemLibrary; +import net.momirealms.customfishing.api.integration.ItemProvider; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; -public class ZaphkielItemImpl implements ItemLibrary { +import java.util.Objects; + +public class ZaphkielItemProvider implements ItemProvider { private final ZapAPI zapAPI; - public ZaphkielItemImpl() { + public ZaphkielItemProvider() { this.zapAPI = Zaphkiel.INSTANCE.api(); } @Override - public String identification() { + public String identifier() { return "Zaphkiel"; } + @NotNull @Override - public ItemStack buildItem(Player player, String id) { - return zapAPI.getItemManager().generateItemStack(id, player); + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { + return Objects.requireNonNull(zapAPI.getItemManager().generateItemStack(id, player)); } @Override - public String getItemID(ItemStack itemStack) { + public String itemID(@NotNull ItemStack itemStack) { if (itemStack == null || itemStack.getType() == Material.AIR) return null; return zapAPI.getItemHandler().getItemId(itemStack); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/AuraSkillsImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/AuraSkillsLevelerProvider.java similarity index 71% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/level/AuraSkillsImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/AuraSkillsLevelerProvider.java index 70847a40..3d514064 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/AuraSkillsImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/AuraSkillsLevelerProvider.java @@ -15,23 +15,29 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.level; +package net.momirealms.customfishing.bukkit.integration.level; import dev.aurelium.auraskills.api.AuraSkillsApi; import dev.aurelium.auraskills.api.registry.NamespacedId; -import net.momirealms.customfishing.api.integration.LevelInterface; +import net.momirealms.customfishing.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public class AuraSkillsImpl implements LevelInterface { +public class AuraSkillsLevelerProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { + public String identifier() { + return "AuraSkills"; + } + + @Override + public void addXp(@NotNull Player player, @NotNull String target, double amount) { AuraSkillsApi.get().getUser(player.getUniqueId()) .addSkillXp(AuraSkillsApi.get().getGlobalRegistry().getSkill(NamespacedId.fromDefault(target)), amount); } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { return AuraSkillsApi.get().getUser(player.getUniqueId()).getSkillLevel( AuraSkillsApi.get().getGlobalRegistry().getSkill(NamespacedId.fromDefault(target)) ); diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/AureliumSkillsImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/AureliumSkillsProvider.java similarity index 69% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/level/AureliumSkillsImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/AureliumSkillsProvider.java index cb25cee6..ef484833 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/AureliumSkillsImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/AureliumSkillsProvider.java @@ -15,28 +15,34 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.level; +package net.momirealms.customfishing.bukkit.integration.level; import com.archyx.aureliumskills.api.AureliumAPI; import com.archyx.aureliumskills.leveler.Leveler; -import net.momirealms.customfishing.api.integration.LevelInterface; +import net.momirealms.customfishing.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public class AureliumSkillsImpl implements LevelInterface { +public class AureliumSkillsProvider implements LevelerProvider { + + @Override + public String identifier() { + return "AureliumSkills"; + } private final Leveler leveler; - public AureliumSkillsImpl() { + public AureliumSkillsProvider() { leveler = AureliumAPI.getPlugin().getLeveler(); } @Override - public void addXp(Player player, String target, double amount) { + public void addXp(@NotNull Player player, @NotNull String target, double amount) { leveler.addXp(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target), amount); } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { return AureliumAPI.getSkillLevel(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target)); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/EcoJobsImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/EcoJobsLevelerProvider.java similarity index 71% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/level/EcoJobsImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/EcoJobsLevelerProvider.java index fb0f8308..1edea7dd 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/EcoJobsImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/EcoJobsLevelerProvider.java @@ -15,18 +15,19 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.level; +package net.momirealms.customfishing.bukkit.integration.level; import com.willfp.ecojobs.api.EcoJobsAPI; import com.willfp.ecojobs.jobs.Job; import com.willfp.ecojobs.jobs.Jobs; -import net.momirealms.customfishing.api.integration.LevelInterface; +import net.momirealms.customfishing.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public class EcoJobsImpl implements LevelInterface { +public class EcoJobsLevelerProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { + public void addXp(@NotNull Player player, @NotNull String target, double amount) { for (Job job : EcoJobsAPI.getActiveJobs(player)) { if (job.getId().equals(target)) { EcoJobsAPI.giveJobExperience(player, job, amount); @@ -35,9 +36,14 @@ public class EcoJobsImpl implements LevelInterface { } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { Job job = Jobs.getByID(target); if (job == null) return 0; return EcoJobsAPI.getJobLevel(player, job); } + + @Override + public String identifier() { + return "EcoJobs"; + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/EcoSkillsImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/EcoSkillsLevelerProvider.java similarity index 69% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/level/EcoSkillsImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/EcoSkillsLevelerProvider.java index 01dc15f7..e36765f2 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/EcoSkillsImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/EcoSkillsLevelerProvider.java @@ -15,24 +15,30 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.level; +package net.momirealms.customfishing.bukkit.integration.level; import com.willfp.ecoskills.api.EcoSkillsAPI; import com.willfp.ecoskills.skills.Skills; -import net.momirealms.customfishing.api.integration.LevelInterface; +import net.momirealms.customfishing.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import java.util.Objects; -public class EcoSkillsImpl implements LevelInterface { +public class EcoSkillsLevelerProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { + public void addXp(@NotNull Player player, @NotNull String target, double amount) { EcoSkillsAPI.gainSkillXP(player, Objects.requireNonNull(Skills.INSTANCE.getByID(target)), amount); } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { return EcoSkillsAPI.getSkillLevel(player, Objects.requireNonNull(Skills.INSTANCE.getByID(target))); } + + @Override + public String identifier() { + return "EcoSkills"; + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/JobsRebornImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/JobsRebornLevelerProvider.java similarity index 77% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/level/JobsRebornImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/JobsRebornLevelerProvider.java index 0302b1a5..a093a83a 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/JobsRebornImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/JobsRebornLevelerProvider.java @@ -15,21 +15,22 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.level; +package net.momirealms.customfishing.bukkit.integration.level; import com.gamingmesh.jobs.Jobs; import com.gamingmesh.jobs.container.Job; import com.gamingmesh.jobs.container.JobProgression; import com.gamingmesh.jobs.container.JobsPlayer; -import net.momirealms.customfishing.api.integration.LevelInterface; +import net.momirealms.customfishing.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import java.util.List; -public class JobsRebornImpl implements LevelInterface { +public class JobsRebornLevelerProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { + public void addXp(@NotNull Player player, @NotNull String target, double amount) { JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player); Job job = Jobs.getJob(target); if (jobsPlayer != null && jobsPlayer.isInJob(job)) @@ -37,7 +38,7 @@ public class JobsRebornImpl implements LevelInterface { } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player); if (jobsPlayer != null) { List jobs = jobsPlayer.getJobProgression(); @@ -48,4 +49,9 @@ public class JobsRebornImpl implements LevelInterface { } return 0; } + + @Override + public String identifier() { + return "JobsReborn"; + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/MMOCoreImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/MMOCoreLevelerProvider.java similarity index 70% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/level/MMOCoreImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/MMOCoreLevelerProvider.java index 2c0691c5..ecbadf20 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/MMOCoreImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/MMOCoreLevelerProvider.java @@ -15,23 +15,29 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.level; +package net.momirealms.customfishing.bukkit.integration.level; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.experience.EXPSource; -import net.momirealms.customfishing.api.integration.LevelInterface; +import net.momirealms.customfishing.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public class MMOCoreImpl implements LevelInterface { +public class MMOCoreLevelerProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { + public String identifier() { + return "MMOCore"; + } + + @Override + public void addXp(@NotNull Player player, @NotNull String target, double amount) { MMOCore.plugin.professionManager.get(target).giveExperience(PlayerData.get(player), amount, null ,EXPSource.OTHER); } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { return PlayerData.get(player).getCollectionSkills().getLevel(MMOCore.plugin.professionManager.get(target)); } } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/McMMOImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/McMMOLevelerProvider.java similarity index 68% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/level/McMMOImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/McMMOLevelerProvider.java index 41ec39f0..174f80a2 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/level/McMMOImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/level/McMMOLevelerProvider.java @@ -15,22 +15,28 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.level; +package net.momirealms.customfishing.bukkit.integration.level; import com.gmail.nossr50.api.ExperienceAPI; import com.gmail.nossr50.datatypes.skills.PrimarySkillType; -import net.momirealms.customfishing.api.integration.LevelInterface; +import net.momirealms.customfishing.api.integration.LevelerProvider; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public class McMMOImpl implements LevelInterface { +public class McMMOLevelerProvider implements LevelerProvider { @Override - public void addXp(Player player, String target, double amount) { + public String identifier() { + return "mcMMO"; + } + + @Override + public void addXp(@NotNull Player player, @NotNull String target, double amount) { ExperienceAPI.addRawXP(player, target, (float) amount, "UNKNOWN"); } @Override - public int getLevel(Player player, String target) { + public int getLevel(@NotNull Player player, @NotNull String target) { return ExperienceAPI.getLevel(player, PrimarySkillType.valueOf(target)); } } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/CompetitionPapi.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CompetitionPapi.java similarity index 75% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/CompetitionPapi.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CompetitionPapi.java index c68b2492..1cdc1904 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/CompetitionPapi.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CompetitionPapi.java @@ -15,12 +15,14 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.papi; +package net.momirealms.customfishing.bukkit.integration.papi; import me.clip.placeholderapi.expansion.PlaceholderExpansion; -import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; -import net.momirealms.customfishing.setting.CFLocale; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.common.locale.MessageConstants; +import net.momirealms.customfishing.common.locale.TranslationManager; import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,9 +31,9 @@ import java.util.Optional; public class CompetitionPapi extends PlaceholderExpansion { - private final CustomFishingPlugin plugin; + private final BukkitCustomFishingPlugin plugin; - public CompetitionPapi(CustomFishingPlugin plugin) { + public CompetitionPapi(BukkitCustomFishingPlugin plugin) { this.plugin = plugin; } @@ -55,7 +57,7 @@ public class CompetitionPapi extends PlaceholderExpansion { @Override public @NotNull String getVersion() { - return "2.0"; + return "2.2"; } @Override @@ -70,25 +72,25 @@ public class CompetitionPapi extends PlaceholderExpansion { return String.valueOf(plugin.getCompetitionManager().getOnGoingCompetition() != null); } case "nextseconds" -> { - return String.valueOf(plugin.getCompetitionManager().getNextCompetitionSeconds()); + return String.valueOf(plugin.getCompetitionManager().getNextCompetitionInSeconds()); } case "nextsecond" -> { - return plugin.getCompetitionManager().getNextCompetitionSeconds() % 60 + CFLocale.FORMAT_Second; + return plugin.getCompetitionManager().getNextCompetitionInSeconds() % 60 + TranslationManager.miniMessageTranslation(MessageConstants.FORMAT_SECOND.build().key()); } case "nextminute" -> { - int sec = plugin.getCompetitionManager().getNextCompetitionSeconds(); + int sec = plugin.getCompetitionManager().getNextCompetitionInSeconds(); int min = (sec % 3600) / 60; - return sec < 60 ? "" : min + CFLocale.FORMAT_Minute; + return sec < 60 ? "" : min + TranslationManager.miniMessageTranslation(MessageConstants.FORMAT_MINUTE.build().key()); } case "nexthour" -> { - int sec = plugin.getCompetitionManager().getNextCompetitionSeconds(); + int sec = plugin.getCompetitionManager().getNextCompetitionInSeconds(); int h = (sec % (3600 * 24)) / 3600; - return sec < 3600 ? "" : h + CFLocale.FORMAT_Hour; + return sec < 3600 ? "" : h + TranslationManager.miniMessageTranslation(MessageConstants.FORMAT_HOUR.build().key()); } case "nextday" -> { - int sec = plugin.getCompetitionManager().getNextCompetitionSeconds(); + int sec = plugin.getCompetitionManager().getNextCompetitionInSeconds(); int day = sec / (3600 * 24); - return day == 0 ? "" : day + CFLocale.FORMAT_Day; + return day == 0 ? "" : day + TranslationManager.miniMessageTranslation(MessageConstants.FORMAT_DAY.build().key()); } case "rank" -> { FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); @@ -98,27 +100,27 @@ public class CompetitionPapi extends PlaceholderExpansion { case "goal" -> { FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); if (competition == null) return ""; - else return competition.getGoal().name(); + else return competition.getGoal().toString(); } case "seconds" -> { FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); if (competition == null) return ""; - return competition.getCachedPlaceholder("{seconds}"); + return String.valueOf(competition.getPublicContext().arg(ContextKeys.SECONDS)); } case "second" -> { FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); if (competition == null) return ""; - return competition.getCachedPlaceholder("{second}"); + return String.valueOf(competition.getPublicContext().arg(ContextKeys.SECOND)); } case "minute" -> { FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); if (competition == null) return ""; - return competition.getCachedPlaceholder("{minute}"); + return String.valueOf(competition.getPublicContext().arg(ContextKeys.MINUTE)); } case "hour" -> { FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); if (competition == null) return ""; - return competition.getCachedPlaceholder("{hour}"); + return String.valueOf(competition.getPublicContext().arg(ContextKeys.HOUR)); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/CFPapi.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CustomFishingPapi.java similarity index 74% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/CFPapi.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CustomFishingPapi.java index d57d56e6..6fe85a2c 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/CFPapi.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/CustomFishingPapi.java @@ -15,22 +15,23 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.papi; +package net.momirealms.customfishing.bukkit.integration.papi; import me.clip.placeholderapi.expansion.PlaceholderExpansion; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.user.OnlineUser; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.storage.user.UserData; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class CFPapi extends PlaceholderExpansion { +public class CustomFishingPapi extends PlaceholderExpansion { - private final CustomFishingPlugin plugin; + private final BukkitCustomFishingPlugin plugin; - public CFPapi(CustomFishingPlugin plugin) { + public CustomFishingPapi(BukkitCustomFishingPlugin plugin) { this.plugin = plugin; } @@ -54,7 +55,7 @@ public class CFPapi extends PlaceholderExpansion { @Override public @NotNull String getVersion() { - return "2.0"; + return "2.2"; } @Override @@ -65,11 +66,9 @@ public class CFPapi extends PlaceholderExpansion { @Override public @Nullable String onRequest(OfflinePlayer offlinePlayer, @NotNull String params) { String[] split = params.split("_"); - Player player = offlinePlayer.getPlayer(); if (player == null) return ""; - switch (split[0]) { case "market" -> { if (split.length < 2) @@ -77,46 +76,45 @@ public class CFPapi extends PlaceholderExpansion { switch (split[1]) { case "limit" -> { if (split.length < 3) { - return String.format("%.2f", plugin.getMarketManager().getEarningLimit(player)); + return String.format("%.2f", plugin.getMarketManager().earningLimit(Context.player(player))); } else { Player another = Bukkit.getPlayer(split[2]); if (another == null) { return ""; } - return String.format("%.2f", plugin.getMarketManager().getEarningLimit(another)); + return String.format("%.2f", plugin.getMarketManager().earningLimit(Context.player(another))); } } case "earnings" -> { - OnlineUser user; + UserData user; if (split.length < 3) { - user = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); + user = plugin.getStorageManager().getOnlineUser(player.getUniqueId()).orElse(null); } else { Player another = Bukkit.getPlayer(split[2]); if (another == null) { return ""; } - user = plugin.getStorageManager().getOnlineUser(another.getUniqueId()); + user = plugin.getStorageManager().getOnlineUser(another.getUniqueId()).orElse(null); } if (user == null) return ""; - return String.format("%.2f", user.getEarningData().earnings); + return String.format("%.2f", user.earningData().earnings()); } case "canearn" -> { if (split.length < 3) { - OnlineUser user = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); + UserData user = plugin.getStorageManager().getOnlineUser(player.getUniqueId()).orElse(null); if (user == null) return ""; - return String.format("%.2f", plugin.getMarketManager().getEarningLimit(player) - user.getEarningData().earnings); + return String.format("%.2f", plugin.getMarketManager().earningLimit(Context.player(player)) - user.earningData().earnings()); } else { Player another = Bukkit.getPlayer(split[2]); if (another == null) { return ""; } - - OnlineUser user = plugin.getStorageManager().getOnlineUser(another.getUniqueId()); + UserData user = plugin.getStorageManager().getOnlineUser(another.getUniqueId()).orElse(null); if (user == null) return ""; - return String.format("%.2f", plugin.getMarketManager().getEarningLimit(another) - user.getEarningData().earnings); + return String.format("%.2f", plugin.getMarketManager().earningLimit(Context.player(another)) - user.earningData().earnings()); } } } diff --git a/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/StatisticsPapi.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/StatisticsPapi.java new file mode 100644 index 00000000..b3e6c801 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/StatisticsPapi.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.integration.papi; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.statistic.FishingStatistics; +import net.momirealms.customfishing.api.storage.user.UserData; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; + +public class StatisticsPapi extends PlaceholderExpansion { + + private final BukkitCustomFishingPlugin plugin; + + public StatisticsPapi(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + } + + public void load() { + super.register(); + } + + public void unload() { + super.unregister(); + } + + @Override + public @NotNull String getIdentifier() { + return "fishingstats"; + } + + @Override + public @NotNull String getAuthor() { + return "XiaoMoMi"; + } + + @Override + public @NotNull String getVersion() { + return "2.2"; + } + + @Override + public boolean persist() { + return true; + } + + @Override + public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) { + Optional onlineUser = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); + return onlineUser.map( + data -> { + FishingStatistics statistics = data.statistics(); + String[] split = params.split("_", 2); + switch (split[0]) { + case "total" -> { + return String.valueOf(statistics.amountOfFishCaught()); + } + case "hascaught" -> { + if (split.length == 1) return "Invalid format"; + return String.valueOf(statistics.getAmount(split[1]) != 0); + } + case "amount" -> { + if (split.length == 1) return "Invalid format"; + return String.valueOf(statistics.getAmount(split[1])); + } + case "size-record" -> { + float size = statistics.getMaxSize(split[1]); + return String.format("%.2f", size < 0 ? 0 : size); + } + case "category" -> { + if (split.length == 1) return "Invalid format"; + String[] categorySplit = split[1].split("_", 2); + if (categorySplit.length == 1) return "Invalid format"; + List category = plugin.getStatisticsManager().getCategoryMembers(categorySplit[1]); + if (categorySplit[0].equals("total")) { + int total = 0; + for (String loot : category) { + total += statistics.getAmount(loot); + } + return String.valueOf(total); + } else if (categorySplit[0].equals("progress")) { + int size = category.size(); + int unlocked = 0; + for (String loot : category) { + if (statistics.getAmount(loot) != 0) unlocked++; + } + double percent = ((double) unlocked * 100) / size; + String progress = String.format("%.1f", percent); + return progress.equals("100.0") ? "100" : progress; + } + } + } + return ""; + } + ).orElse("Data not loaded"); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/quest/BattlePassHook.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/quest/BattlePassQuest.java similarity index 84% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/quest/BattlePassHook.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/quest/BattlePassQuest.java index 2983460d..2c0f1fee 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/quest/BattlePassHook.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/quest/BattlePassQuest.java @@ -15,13 +15,13 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.quest; +package net.momirealms.customfishing.bukkit.integration.quest; import io.github.battlepass.BattlePlugin; import io.github.battlepass.api.events.server.PluginReloadEvent; import net.advancedplugins.bp.impl.actions.ActionRegistry; import net.advancedplugins.bp.impl.actions.external.executor.ActionQuestExecutor; -import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; import net.momirealms.customfishing.api.event.FishingResultEvent; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -29,10 +29,10 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.plugin.java.JavaPlugin; -public class BattlePassHook implements Listener { +public class BattlePassQuest implements Listener { - public BattlePassHook() { - Bukkit.getPluginManager().registerEvents(this, CustomFishingPlugin.get()); + public BattlePassQuest() { + Bukkit.getPluginManager().registerEvents(this, BukkitCustomFishingPlugin.getInstance().getBoostrap()); } public void register() { @@ -57,15 +57,15 @@ public class BattlePassHook implements Listener { Player player = event.getPlayer(); // Single Fish Quest - if (event.getLoot().getID() != null) + if (event.getLoot().id() != null) this.executionBuilder("loot") .player(player) - .root(event.getLoot().getID()) + .root(event.getLoot().id()) .progress(event.getAmount()) .buildAndExecute(); // Group Fish Quest - String[] lootGroup = event.getLoot().getLootGroup(); + String[] lootGroup = event.getLoot().lootGroup(); if (event.getLoot() != null && lootGroup != null) for (String group : lootGroup) { this.executionBuilder("group") diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/quest/BetonQuestHook.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/quest/BetonQuestQuest.java similarity index 94% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/quest/BetonQuestHook.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/quest/BetonQuestQuest.java index 15fc7859..e83752ea 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/quest/BetonQuestHook.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/quest/BetonQuestQuest.java @@ -15,10 +15,9 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.quest; +package net.momirealms.customfishing.bukkit.integration.quest; import net.momirealms.customfishing.api.event.FishingResultEvent; -import net.momirealms.customfishing.api.util.LogUtils; import org.betonquest.betonquest.BetonQuest; import org.betonquest.betonquest.Instruction; import org.betonquest.betonquest.VariableNumber; @@ -39,7 +38,7 @@ import java.util.Collections; import java.util.HashSet; @SuppressWarnings("DuplicatedCode") -public class BetonQuestHook { +public class BetonQuestQuest { public static void register() { BetonQuest.getInstance().registerObjectives("customfishing_loot", IDObjective.class); @@ -80,7 +79,7 @@ public class BetonQuestHook { if (isInvalidLocation(event, onlineProfile)) { return; } - if (this.loot_ids.contains(event.getLoot().getID()) && this.checkConditions(onlineProfile)) { + if (this.loot_ids.contains(event.getLoot().id()) && this.checkConditions(onlineProfile)) { getCountingData(onlineProfile).progress(event.getAmount()); completeIfDoneOrNotify(onlineProfile); } @@ -96,7 +95,7 @@ public class BetonQuestHook { try { targetLocation = playerLocation.getLocation(profile); } catch (final org.betonquest.betonquest.exceptions.QuestRuntimeException e) { - LogUtils.warn(e.getMessage()); + e.printStackTrace(); return true; } final int range = rangeVar.getInt(profile); @@ -149,7 +148,7 @@ public class BetonQuestHook { if (isInvalidLocation(event, onlineProfile)) { return; } - String[] groups = event.getLoot().getLootGroup(); + String[] groups = event.getLoot().lootGroup(); if (groups != null) for (String group : groups) { if (this.loot_groups.contains(group) && this.checkConditions(onlineProfile)) { @@ -170,7 +169,7 @@ public class BetonQuestHook { try { targetLocation = playerLocation.getLocation(profile); } catch (final org.betonquest.betonquest.exceptions.QuestRuntimeException e) { - LogUtils.warn(e.getMessage()); + e.printStackTrace(); return true; } final int range = rangeVar.getInt(profile); diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/quest/ClueScrollsHook.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/quest/ClueScrollsQuest.java similarity index 73% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/quest/ClueScrollsHook.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/quest/ClueScrollsQuest.java index eb328635..2f753f04 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/quest/ClueScrollsHook.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/quest/ClueScrollsQuest.java @@ -15,10 +15,10 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.quest; +package net.momirealms.customfishing.bukkit.integration.quest; import com.electro2560.dev.cluescrolls.api.*; -import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; import net.momirealms.customfishing.api.event.FishingResultEvent; import net.momirealms.customfishing.api.mechanic.loot.Loot; import org.bukkit.Bukkit; @@ -26,18 +26,18 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -public class ClueScrollsHook implements Listener { +public class ClueScrollsQuest implements Listener { private final CustomClue idClue; private final CustomClue groupClue; - public ClueScrollsHook() { - idClue = ClueScrollsAPI.getInstance().registerCustomClue(CustomFishingPlugin.getInstance(), "loot", new ClueConfigData("id", DataType.STRING)); - groupClue = ClueScrollsAPI.getInstance().registerCustomClue(CustomFishingPlugin.getInstance(), "group", new ClueConfigData("group", DataType.STRING)); + public ClueScrollsQuest() { + idClue = ClueScrollsAPI.getInstance().registerCustomClue(BukkitCustomFishingPlugin.getInstance().getBoostrap(), "loot", new ClueConfigData("id", DataType.STRING)); + groupClue = ClueScrollsAPI.getInstance().registerCustomClue(BukkitCustomFishingPlugin.getInstance().getBoostrap(), "group", new ClueConfigData("group", DataType.STRING)); } public void register() { - Bukkit.getPluginManager().registerEvents(this, CustomFishingPlugin.get()); + Bukkit.getPluginManager().registerEvents(this, BukkitCustomFishingPlugin.getInstance().getBoostrap()); } @EventHandler @@ -57,12 +57,12 @@ public class ClueScrollsHook implements Listener { idClue.handle( player, event.getAmount(), - new ClueDataPair("id", loot.getID()) + new ClueDataPair("id", loot.id()) ); } - if (loot != null && loot.getLootGroup() != null) { - for (String group : event.getLoot().getLootGroup()) { + if (loot != null && loot.lootGroup() != null) { + for (String group : event.getLoot().lootGroup()) { groupClue.handle( player, event.getAmount(), diff --git a/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/season/AdvancedSeasonsProvider.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/season/AdvancedSeasonsProvider.java new file mode 100644 index 00000000..9749a750 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/season/AdvancedSeasonsProvider.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.integration.season; + +import net.advancedplugins.seasons.api.AdvancedSeasonsAPI; +import net.momirealms.customfishing.api.integration.SeasonProvider; +import net.momirealms.customfishing.api.mechanic.misc.season.Season; +import org.bukkit.World; +import org.jetbrains.annotations.NotNull; + +public class AdvancedSeasonsProvider implements SeasonProvider { + + private final AdvancedSeasonsAPI api; + + public AdvancedSeasonsProvider() { + this.api = new AdvancedSeasonsAPI(); + } + + @NotNull + @Override + public Season getSeason(@NotNull World world) { + return switch (api.getSeason(world)) { + case "SPRING" -> Season.SPRING; + case "WINTER" -> Season.WINTER; + case "SUMMER" -> Season.SUMMER; + case "FALL" -> Season.AUTUMN; + default -> Season.DISABLE; + }; + } + + @Override + public String identifier() { + return "AdvancedSeasons"; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/season/CustomCropsSeasonImpl.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/season/CustomCropsSeasonProvider.java similarity index 60% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/season/CustomCropsSeasonImpl.java rename to compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/season/CustomCropsSeasonProvider.java index 837db4d7..ce5a2856 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/season/CustomCropsSeasonImpl.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/season/CustomCropsSeasonProvider.java @@ -15,23 +15,28 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.season; +package net.momirealms.customfishing.bukkit.integration.season; import net.momirealms.customcrops.api.CustomCropsPlugin; import net.momirealms.customcrops.api.mechanic.world.season.Season; -import net.momirealms.customfishing.api.integration.SeasonInterface; +import net.momirealms.customfishing.api.integration.SeasonProvider; import org.bukkit.World; import org.jetbrains.annotations.NotNull; import java.util.Locale; -public class CustomCropsSeasonImpl implements SeasonInterface { +public class CustomCropsSeasonProvider implements SeasonProvider { @NotNull @Override - public String getSeason(World world) { - Season season = CustomCropsPlugin.get().getIntegrationManager().getSeason(world); - if (season == null) return "disabled"; - return season.name().toUpperCase(Locale.ENGLISH); + public net.momirealms.customfishing.api.mechanic.misc.season.Season getSeason(@NotNull World world) { + Season season = CustomCropsPlugin.get().getIntegrationManager().getSeasonInterface().getSeason(world); + if (season == null) return net.momirealms.customfishing.api.mechanic.misc.season.Season.DISABLE; + return net.momirealms.customfishing.api.mechanic.misc.season.Season.valueOf(season.name().toUpperCase(Locale.ENGLISH)); + } + + @Override + public String identifier() { + return "CustomCrops"; } } \ No newline at end of file diff --git a/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/season/RealisticSeasonsProvider.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/season/RealisticSeasonsProvider.java new file mode 100644 index 00000000..f62cc591 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/season/RealisticSeasonsProvider.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.integration.season; + +import me.casperge.realisticseasons.api.SeasonsAPI; +import net.momirealms.customfishing.api.integration.SeasonProvider; +import org.bukkit.World; +import org.jetbrains.annotations.NotNull; + +public class RealisticSeasonsProvider implements SeasonProvider { + + @NotNull + @Override + public net.momirealms.customfishing.api.mechanic.misc.season.Season getSeason(@NotNull World world) { + return switch (SeasonsAPI.getInstance().getSeason(world)) { + case WINTER -> net.momirealms.customfishing.api.mechanic.misc.season.Season.WINTER; + case SPRING -> net.momirealms.customfishing.api.mechanic.misc.season.Season.SPRING; + case SUMMER -> net.momirealms.customfishing.api.mechanic.misc.season.Season.SUMMER; + case FALL -> net.momirealms.customfishing.api.mechanic.misc.season.Season.AUTUMN; + case DISABLED, RESTORE -> net.momirealms.customfishing.api.mechanic.misc.season.Season.DISABLE; + }; + } + + @Override + public String identifier() { + return "RealisticSeasons"; + } +} diff --git a/plugin/.gitignore b/core/.gitignore similarity index 100% rename from plugin/.gitignore rename to core/.gitignore diff --git a/core/build.gradle.kts b/core/build.gradle.kts new file mode 100644 index 00000000..737b6f68 --- /dev/null +++ b/core/build.gradle.kts @@ -0,0 +1,101 @@ +val commitID: String by project + +plugins { + id("io.github.goooler.shadow") version "8.1.7" +} + +repositories { + maven("https://repo.xenondevs.xyz/releases") // invui + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // papi +} + +dependencies { + // platform + compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT") + // subprojects + implementation(project(":common")) + implementation(project(":api")) { + exclude("dev.dejvokep", "boosted-yaml") + } + implementation(project(":compatibility")) + // adventure + implementation("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}") + implementation("net.kyori:adventure-text-minimessage:${rootProject.properties["adventure_bundle_version"]}") + implementation("net.kyori:adventure-platform-bukkit:${rootProject.properties["adventure_platform_version"]}") + implementation("net.kyori:adventure-text-serializer-gson:${rootProject.properties["adventure_bundle_version"]}") { + exclude("com.google.code.gson", "gson") + } + // GUI + implementation("xyz.xenondevs.invui:invui:${rootProject.properties["invui_version"]}") { + exclude("org.jetbrains", "annotations") + } + // tag & component + implementation("com.saicone.rtag:rtag:${rootProject.properties["rtag_version"]}") + implementation("com.saicone.rtag:rtag-item:${rootProject.properties["rtag_version"]}") + // nms util + implementation("com.github.Xiao-MoMi:Sparrow-Heart:${rootProject.properties["sparrow_heart_version"]}") +// implementation(files("libs/Sparrow-Heart-${rootProject.properties["sparrow_heart_version"]}.jar")) + // bstats + compileOnly("org.bstats:bstats-bukkit:${rootProject.properties["bstats_version"]}") + // config + compileOnly("dev.dejvokep:boosted-yaml:${rootProject.properties["boosted_yaml_version"]}") + // serialization + compileOnly("com.google.code.gson:gson:${rootProject.properties["gson_version"]}") + // database + compileOnly("org.xerial:sqlite-jdbc:${rootProject.properties["sqlite_driver_version"]}") + compileOnly("com.h2database:h2:${rootProject.properties["h2_driver_version"]}") + compileOnly("org.mongodb:mongodb-driver-sync:${rootProject.properties["mongodb_driver_version"]}") + compileOnly("com.zaxxer:HikariCP:${rootProject.properties["hikari_version"]}") + compileOnly("redis.clients:jedis:${rootProject.properties["jedis_version"]}") + // cloud command framework + compileOnly("org.incendo:cloud-core:${rootProject.properties["cloud_core_version"]}") + compileOnly("org.incendo:cloud-minecraft-extras:${rootProject.properties["cloud_minecraft_extras_version"]}") + compileOnly("org.incendo:cloud-paper:${rootProject.properties["cloud_paper_version"]}") + // expression + compileOnly("net.objecthunter:exp4j:${rootProject.properties["exp4j_version"]}") + // placeholder api + compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}") + // lz4 + compileOnly("org.lz4:lz4-java:${rootProject.properties["lz4_version"]}") +} + +tasks { + shadowJar { + archiveFileName = "CustomFishing-${rootProject.properties["project_version"]}-${commitID}.jar" + destinationDirectory.set(file("$rootDir/target")) + relocate("net.kyori", "net.momirealms.customfishing.libraries") + relocate("org.incendo", "net.momirealms.customfishing.libraries") + relocate("dev.dejvokep", "net.momirealms.customfishing.libraries") + relocate ("org.apache.commons.pool2", "net.momirealms.customfishing.libraries.commonspool2") + relocate ("com.mysql", "net.momirealms.customfishing.libraries.mysql") + relocate ("org.mariadb", "net.momirealms.customfishing.libraries.mariadb") + relocate ("com.zaxxer.hikari", "net.momirealms.customfishing.libraries.hikari") + relocate ("com.mongodb", "net.momirealms.customfishing.libraries.mongodb") + relocate ("org.bson", "net.momirealms.customfishing.libraries.bson") + relocate ("org.bstats", "net.momirealms.customfishing.libraries.bstats") + relocate ("com.github.benmanes.caffeine", "net.momirealms.customfishing.libraries.caffeine") + relocate ("net.momirealms.sparrow.heart", "net.momirealms.customfishing.bukkit.nms") + relocate ("com.saicone.rtag", "net.momirealms.customfishing.libraries.rtag") + relocate ("xyz.xenondevs", "net.momirealms.customfishing.libraries") + relocate ("net.objecthunter.exp4j", "net.momirealms.customfishing.libraries.exp4j") + relocate ("net.jpountz", "net.momirealms.customfishing.libraries.jpountz") //lz4 + } +} + +artifacts { + archives(tasks.shadowJar) +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) +} \ No newline at end of file diff --git a/core/libs/Sparrow-Heart-0.29.jar b/core/libs/Sparrow-Heart-0.29.jar new file mode 100644 index 00000000..6bcee317 Binary files /dev/null and b/core/libs/Sparrow-Heart-0.29.jar differ diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/BukkitBootstrap.java b/core/src/main/java/net/momirealms/customfishing/bukkit/BukkitBootstrap.java new file mode 100644 index 00000000..5e5441f4 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/BukkitBootstrap.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import org.bukkit.plugin.java.JavaPlugin; + +public class BukkitBootstrap extends JavaPlugin { + + private BukkitCustomFishingPlugin plugin; + + @Override + public void onLoad() { + this.plugin = new BukkitCustomFishingPluginImpl(this); + this.plugin.load(); + } + + @Override + public void onEnable() { + this.plugin.enable(); + } + + @Override + public void onDisable() { + this.plugin.disable(); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/BukkitCustomFishingPluginImpl.java b/core/src/main/java/net/momirealms/customfishing/bukkit/BukkitCustomFishingPluginImpl.java new file mode 100644 index 00000000..000e088e --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/BukkitCustomFishingPluginImpl.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.event.CustomFishingReloadEvent; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.misc.cooldown.CoolDownManager; +import net.momirealms.customfishing.api.mechanic.misc.placeholder.BukkitPlaceholderManager; +import net.momirealms.customfishing.api.util.EventUtils; +import net.momirealms.customfishing.bukkit.action.BukkitActionManager; +import net.momirealms.customfishing.bukkit.bag.BukkitBagManager; +import net.momirealms.customfishing.bukkit.block.BukkitBlockManager; +import net.momirealms.customfishing.bukkit.command.BukkitCommandManager; +import net.momirealms.customfishing.bukkit.competition.BukkitCompetitionManager; +import net.momirealms.customfishing.bukkit.config.BukkitConfigManager; +import net.momirealms.customfishing.bukkit.effect.BukkitEffectManager; +import net.momirealms.customfishing.bukkit.entity.BukkitEntityManager; +import net.momirealms.customfishing.bukkit.event.BukkitEventManager; +import net.momirealms.customfishing.bukkit.fishing.BukkitFishingManager; +import net.momirealms.customfishing.bukkit.game.BukkitGameManager; +import net.momirealms.customfishing.bukkit.hook.BukkitHookManager; +import net.momirealms.customfishing.bukkit.integration.BukkitIntegrationManager; +import net.momirealms.customfishing.bukkit.item.BukkitItemManager; +import net.momirealms.customfishing.bukkit.loot.BukkitLootManager; +import net.momirealms.customfishing.bukkit.market.BukkitMarketManager; +import net.momirealms.customfishing.bukkit.requirement.BukkitRequirementManager; +import net.momirealms.customfishing.bukkit.scheduler.BukkitSchedulerAdapter; +import net.momirealms.customfishing.bukkit.sender.BukkitSenderFactory; +import net.momirealms.customfishing.bukkit.statistic.BukkitStatisticsManager; +import net.momirealms.customfishing.bukkit.storage.BukkitStorageManager; +import net.momirealms.customfishing.bukkit.totem.BukkitTotemManager; +import net.momirealms.customfishing.common.dependency.Dependency; +import net.momirealms.customfishing.common.dependency.DependencyManagerImpl; +import net.momirealms.customfishing.common.helper.VersionHelper; +import net.momirealms.customfishing.common.locale.TranslationManager; +import net.momirealms.customfishing.common.plugin.classpath.ClassPathAppender; +import net.momirealms.customfishing.common.plugin.classpath.ReflectionClassPathAppender; +import net.momirealms.customfishing.common.plugin.logging.JavaPluginLogger; +import net.momirealms.customfishing.common.plugin.logging.PluginLogger; +import org.bstats.bukkit.Metrics; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.InputStream; +import java.nio.file.Path; +import java.util.List; +import java.util.function.Consumer; + +public class BukkitCustomFishingPluginImpl extends BukkitCustomFishingPlugin { + + private final ClassPathAppender classPathAppender; + private final PluginLogger logger; + private BukkitCommandManager commandManager; + private Consumer debugger; + + public BukkitCustomFishingPluginImpl(Plugin boostrap) { + super(boostrap); + VersionHelper.init(getServerVersion()); + this.scheduler = new BukkitSchedulerAdapter(this); + this.classPathAppender = new ReflectionClassPathAppender(this); + this.logger = new JavaPluginLogger(getBoostrap().getLogger()); + this.dependencyManager = new DependencyManagerImpl(this); + } + + @Override + public void load() { + this.dependencyManager.loadDependencies( + List.of( + Dependency.BOOSTED_YAML, + Dependency.BSTATS_BASE, Dependency.BSTATS_BUKKIT, + Dependency.CAFFEINE, + Dependency.GEANTY_REF, + Dependency.CLOUD_CORE, Dependency.CLOUD_SERVICES, Dependency.CLOUD_BUKKIT, Dependency.CLOUD_PAPER, Dependency.CLOUD_BRIGADIER, Dependency.CLOUD_MINECRAFT_EXTRAS, + Dependency.GSON, + Dependency.COMMONS_POOL_2, + Dependency.JEDIS, + Dependency.EXP4J, + Dependency.MYSQL_DRIVER, Dependency.MARIADB_DRIVER, + Dependency.SQLITE_DRIVER, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, + Dependency.H2_DRIVER, + Dependency.MONGODB_DRIVER_CORE, Dependency.MONGODB_DRIVER_SYNC, Dependency.MONGODB_DRIVER_BSON, + Dependency.HIKARI_CP, + Dependency.LZ4 + ) + ); + } + + @Override + public void enable() { + this.eventManager = new BukkitEventManager(this); + this.configManager = new BukkitConfigManager(this); + this.requirementManager = new BukkitRequirementManager(this); + this.actionManager = new BukkitActionManager(this); + this.senderFactory = new BukkitSenderFactory(this); + this.placeholderManager = new BukkitPlaceholderManager(this); + this.itemManager = new BukkitItemManager(this); + this.competitionManager = new BukkitCompetitionManager(this); + this.marketManager = new BukkitMarketManager(this); + this.storageManager = new BukkitStorageManager(this); + this.lootManager = new BukkitLootManager(this); + this.coolDownManager = new CoolDownManager(this); + this.entityManager = new BukkitEntityManager(this); + this.blockManager = new BukkitBlockManager(this); + this.statisticsManager = new BukkitStatisticsManager(this); + this.effectManager = new BukkitEffectManager(this); + this.hookManager = new BukkitHookManager(this); + this.fishingManager = new BukkitFishingManager(this); + this.bagManager = new BukkitBagManager(this); + this.totemManager = new BukkitTotemManager(this); + this.translationManager = new TranslationManager(this); + this.integrationManager = new BukkitIntegrationManager(this); + this.gameManager = new BukkitGameManager(this); + this.commandManager = new BukkitCommandManager(this); + this.commandManager.registerDefaultFeatures(); + + this.reload(); + if (ConfigManager.metrics()) new Metrics((JavaPlugin) getBoostrap(), 16648); + if (ConfigManager.checkUpdate()) { + VersionHelper.UPDATE_CHECKER.apply(this).thenAccept(result -> { + if (!result) this.getPluginLogger().info("You are using the latest version."); + else this.getPluginLogger().warn("Update is available: https://polymart.org/resource/2723"); + }); + } + } + + @Override + public void reload() { + MechanicType.reset(); + + this.itemManager.unload(); + this.eventManager.unload(); + this.entityManager.unload(); + this.lootManager.unload(); + this.blockManager.unload(); + this.effectManager.unload(); + this.hookManager.unload(); + this.totemManager.unload(); + this.gameManager.unload(); + + // before ConfigManager + this.placeholderManager.reload(); + this.configManager.reload(); + // after ConfigManager + this.debugger = ConfigManager.debug() ? (s) -> logger.info("[DEBUG] " + s.toString()) : (s) -> {}; + + this.actionManager.reload(); + this.requirementManager.reload(); + this.coolDownManager.reload(); + this.translationManager.reload(); + this.marketManager.reload(); + this.competitionManager.reload(); + this.statisticsManager.reload(); + this.bagManager.reload(); + this.storageManager.reload(); + this.fishingManager.reload(); + + this.itemManager.load(); + this.eventManager.load(); + this.entityManager.load(); + this.lootManager.load(); + this.blockManager.load(); + this.effectManager.load(); + this.hookManager.load(); + this.totemManager.load(); + this.gameManager.load(); + + EventUtils.fireAndForget(new CustomFishingReloadEvent(this)); + } + + @Override + public void disable() { + this.eventManager.disable(); + this.configManager.disable(); + this.requirementManager.disable(); + this.actionManager.disable(); + this.placeholderManager.disable(); + this.itemManager.disable(); + this.competitionManager.disable(); + this.marketManager.disable(); + this.lootManager.disable(); + this.coolDownManager.disable(); + this.entityManager.disable(); + this.blockManager.disable(); + this.statisticsManager.disable(); + this.effectManager.disable(); + this.hookManager.disable(); + this.bagManager.disable(); + this.integrationManager.disable(); + this.storageManager.disable(); + this.commandManager.unregisterFeatures(); + } + + @Override + public InputStream getResourceStream(String filePath) { + return getBoostrap().getResource(filePath); + } + + @Override + public PluginLogger getPluginLogger() { + return logger; + } + + @Override + public ClassPathAppender getClassPathAppender() { + return classPathAppender; + } + + @Override + public Path getDataDirectory() { + return getBoostrap().getDataFolder().toPath().toAbsolutePath(); + } + + @Override + public String getServerVersion() { + return Bukkit.getServer().getBukkitVersion().split("-")[0]; + } + + @SuppressWarnings("deprecation") + @Override + public String getPluginVersion() { + return getBoostrap().getDescription().getVersion(); + } + + @Override + public void debug(Object message) { + this.debugger.accept(message); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/action/BukkitActionManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/action/BukkitActionManager.java new file mode 100644 index 00000000..635e58bd --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/action/BukkitActionManager.java @@ -0,0 +1,938 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.action; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.action.*; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.misc.placeholder.BukkitPlaceholderManager; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import net.momirealms.customfishing.api.mechanic.misc.value.TextValue; +import net.momirealms.customfishing.api.mechanic.requirement.Requirement; +import net.momirealms.customfishing.bukkit.integration.VaultHook; +import net.momirealms.customfishing.bukkit.util.LocationUtils; +import net.momirealms.customfishing.bukkit.util.PlayerUtils; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import net.momirealms.customfishing.common.locale.MessageConstants; +import net.momirealms.customfishing.common.locale.TranslationManager; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customfishing.common.util.ClassUtils; +import net.momirealms.customfishing.common.util.ListUtils; +import net.momirealms.customfishing.common.util.Pair; +import net.momirealms.customfishing.common.util.RandomUtils; +import net.momirealms.sparrow.heart.SparrowHeart; +import net.momirealms.sparrow.heart.feature.armorstand.FakeArmorStand; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.ExperienceOrb; +import org.bukkit.entity.Player; +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; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import static java.util.Objects.requireNonNull; + +public class BukkitActionManager implements ActionManager { + + private final BukkitCustomFishingPlugin plugin; + private final HashMap> actionFactoryMap = new HashMap<>(); + private static final String EXPANSION_FOLDER = "expansions/action"; + + public BukkitActionManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + this.registerBuiltInActions(); + } + + @Override + public void disable() { + this.actionFactoryMap.clear(); + } + + @Override + public void reload() { + this.loadExpansions(); + } + + @Override + public boolean registerAction(String type, ActionFactory actionFactory) { + if (this.actionFactoryMap.containsKey(type)) return false; + this.actionFactoryMap.put(type, actionFactory); + return true; + } + + @Override + public boolean unregisterAction(String type) { + return this.actionFactoryMap.remove(type) != null; + } + + @Nullable + @Override + public ActionFactory getActionFactory(@NotNull String type) { + return actionFactoryMap.get(type); + } + + @Override + public boolean hasAction(@NotNull String type) { + return actionFactoryMap.containsKey(type); + } + + @Override + public Action parseAction(Section section) { + if (section == null) return EmptyAction.INSTANCE; + ActionFactory factory = getActionFactory(section.getString("type")); + if (factory == null) { + plugin.getPluginLogger().warn("Action type: " + section.getString("type") + " doesn't exist."); + return EmptyAction.INSTANCE; + } + return factory.process(section.get("value"), section.getDouble("chance", 1d)); + } + + @NotNull + @Override + @SuppressWarnings("unchecked") + public Action[] parseActions(Section section) { + ArrayList> actionList = new ArrayList<>(); + if (section != null) + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + Action action = parseAction(innerSection); + if (action != null) + actionList.add(action); + } + } + return actionList.toArray(new Action[0]); + } + + @Override + public Action parseAction(@NotNull String type, @NotNull Object args) { + ActionFactory factory = getActionFactory(type); + if (factory == null) { + plugin.getPluginLogger().warn("Action type: " + type + " doesn't exist."); + return EmptyAction.INSTANCE; + } + return factory.process(args, 1); + } + + private void registerBuiltInActions() { + this.registerMessageAction(); + this.registerCommandAction(); + this.registerActionBarAction(); + this.registerCloseInvAction(); + this.registerExpAction(); + this.registerFoodAction(); + this.registerChainAction(); + this.registerMoneyAction(); + this.registerItemAction(); + this.registerPotionAction(); + this.registerFishFindAction(); + this.registerPluginExpAction(); + this.registerSoundAction(); + this.registerHologramAction(); + this.registerFakeItemAction(); + this.registerTitleAction(); + } + + private void registerMessageAction() { + registerAction("message", (args, chance) -> { + List messages = ListUtils.toList(args); + return context -> { + if (Math.random() > chance) return; + List replaced = plugin.getPlaceholderManager().parse(context.getHolder(), messages, context.placeholderMap()); + Audience audience = plugin.getSenderFactory().getAudience(context.getHolder()); + for (String text : replaced) { + audience.sendMessage(AdventureHelper.miniMessage(text)); + } + }; + }); + registerAction("random-message", (args, chance) -> { + List messages = ListUtils.toList(args); + return context -> { + if (Math.random() > chance) return; + String random = messages.get(RandomUtils.generateRandomInt(0, messages.size() - 1)); + random = BukkitPlaceholderManager.getInstance().parse(context.getHolder(), random, context.placeholderMap()); + Audience audience = plugin.getSenderFactory().getAudience(context.getHolder()); + audience.sendMessage(AdventureHelper.miniMessage(random)); + }; + }); + registerAction("broadcast", (args, chance) -> { + List messages = ListUtils.toList(args); + return context -> { + if (Math.random() > chance) return; + List replaced = plugin.getPlaceholderManager().parse(context.getHolder(), messages, context.placeholderMap()); + for (Player player : Bukkit.getOnlinePlayers()) { + Audience audience = plugin.getSenderFactory().getAudience(player); + for (String text : replaced) { + audience.sendMessage(AdventureHelper.miniMessage(text)); + } + } + }; + }); + registerAction("message-nearby", (args, chance) -> { + if (args instanceof Section section) { + List messages = ListUtils.toList(section.get("message")); + MathValue range = MathValue.auto(section.get("range")); + return context -> { + if (Math.random() > chance) return; + double realRange = range.evaluate(context); + Player owner = context.getHolder(); + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + plugin.getScheduler().sync().run(() -> { + for (Entity player : location.getWorld().getNearbyEntities(location, realRange, realRange, realRange, entity -> entity instanceof Player)) { + double distance = LocationUtils.getDistance(player.getLocation(), location); + if (distance <= realRange) { + context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); + List replaced = BukkitPlaceholderManager.getInstance().parse( + owner, + messages, + context.placeholderMap() + ); + Audience audience = plugin.getSenderFactory().getAudience(player); + for (String text : replaced) { + audience.sendMessage(AdventureHelper.miniMessage(text)); + } + } + } + }, location); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at message-nearby action which should be Section"); + return EmptyAction.INSTANCE; + } + }); + } + + private void registerCommandAction() { + registerAction("command", (args, chance) -> { + List commands = ListUtils.toList(args); + return context -> { + if (Math.random() > chance) return; + List replaced = BukkitPlaceholderManager.getInstance().parse(context.getHolder(), commands, context.placeholderMap()); + plugin.getScheduler().sync().run(() -> { + for (String text : replaced) { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text); + } + }, context.arg(ContextKeys.LOCATION)); + }; + }); + registerAction("player-command", (args, chance) -> { + List commands = ListUtils.toList(args); + return context -> { + if (Math.random() > chance) return; + List replaced = BukkitPlaceholderManager.getInstance().parse(context.getHolder(), commands, context.placeholderMap()); + plugin.getScheduler().sync().run(() -> { + for (String text : replaced) { + context.getHolder().performCommand(text); + } + }, context.arg(ContextKeys.LOCATION)); + }; + }); + registerAction("random-command", (args, chance) -> { + List commands = ListUtils.toList(args); + return context -> { + if (Math.random() > chance) return; + String random = commands.get(ThreadLocalRandom.current().nextInt(commands.size())); + random = BukkitPlaceholderManager.getInstance().parse(context.getHolder(), random, context.placeholderMap()); + String finalRandom = random; + plugin.getScheduler().sync().run(() -> { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalRandom); + }, context.arg(ContextKeys.LOCATION)); + }; + }); + registerAction("command-nearby", (args, chance) -> { + if (args instanceof Section section) { + List cmd = ListUtils.toList(section.get("command")); + MathValue range = MathValue.auto(section.get("range")); + return context -> { + if (Math.random() > chance) return; + Player owner = context.getHolder(); + double realRange = range.evaluate(context); + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + plugin.getScheduler().sync().run(() -> { + for (Entity player : location.getWorld().getNearbyEntities(location, realRange, realRange, realRange, entity -> entity instanceof Player)) { + double distance = LocationUtils.getDistance(player.getLocation(), location); + if (distance <= realRange) { + context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); + List replaced = BukkitPlaceholderManager.getInstance().parse(owner, cmd, context.placeholderMap()); + for (String text : replaced) { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text); + } + } + } + }, location); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at command-nearby action which should be Section"); + return EmptyAction.INSTANCE; + } + }); + } + + private void registerCloseInvAction() { + registerAction("close-inv", (args, chance) -> condition -> { + if (Math.random() > chance) return; + condition.getHolder().closeInventory(); + }); + } + + private void registerActionBarAction() { + registerAction("actionbar", (args, chance) -> { + String text = (String) args; + return context -> { + if (Math.random() > chance) return; + Audience audience = plugin.getSenderFactory().getAudience(context.getHolder()); + Component component = AdventureHelper.miniMessage(plugin.getPlaceholderManager().parse(context.getHolder(), text, context.placeholderMap())); + audience.sendActionBar(component); + }; + }); + registerAction("random-actionbar", (args, chance) -> { + List texts = ListUtils.toList(args); + return context -> { + if (Math.random() > chance) return; + String random = texts.get(RandomUtils.generateRandomInt(0, texts.size() - 1)); + random = plugin.getPlaceholderManager().parse(context.getHolder(), random, context.placeholderMap()); + Audience audience = plugin.getSenderFactory().getAudience(context.getHolder()); + audience.sendActionBar(AdventureHelper.miniMessage(random)); + }; + }); + registerAction("actionbar-nearby", (args, chance) -> { + if (args instanceof Section section) { + String actionbar = section.getString("actionbar"); + MathValue range = MathValue.auto(section.get("range")); + return context -> { + if (Math.random() > chance) return; + Player owner = context.getHolder(); + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + double realRange = range.evaluate(context); + plugin.getScheduler().sync().run(() -> { + for (Entity player : location.getWorld().getNearbyEntities(location, realRange, realRange, realRange, entity -> entity instanceof Player)) { + double distance = LocationUtils.getDistance(player.getLocation(), location); + if (distance <= realRange) { + context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); + String replaced = plugin.getPlaceholderManager().parse(owner, actionbar, context.placeholderMap()); + Audience audience = plugin.getSenderFactory().getAudience(player); + audience.sendActionBar(AdventureHelper.miniMessage(replaced)); + } + } + }, location + ); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at actionbar-nearby action which should be Section"); + return EmptyAction.INSTANCE; + } + }); + } + + private void registerExpAction() { + registerAction("mending", (args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (Math.random() > chance) return; + final Player player = context.getHolder(); + player.getLocation().getWorld().spawn(player.getLocation().clone().add(0,0.5,0), ExperienceOrb.class, e -> e.setExperience((int) value.evaluate(context))); + }; + }); + registerAction("exp", (args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (Math.random() > chance) return; + final Player player = context.getHolder(); + player.giveExp((int) Math.round(value.evaluate(context))); + Audience audience = plugin.getSenderFactory().getAudience(player); + AdventureHelper.playSound(audience, Sound.sound(Key.key("minecraft:entity.experience_orb.pickup"), Sound.Source.PLAYER, 1, 1)); + }; + }); + registerAction("level", (args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (Math.random() > chance) return; + Player player = context.getHolder(); + player.setLevel((int) Math.max(0, player.getLevel() + value.evaluate(context))); + }; + }); + } + + private void registerFoodAction() { + registerAction("food", (args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (Math.random() > chance) return; + Player player = context.getHolder(); + player.setFoodLevel((int) (player.getFoodLevel() + value.evaluate(context))); + }; + }); + registerAction("saturation", (args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (Math.random() > chance) return; + Player player = context.getHolder(); + player.setSaturation((float) (player.getSaturation() + value.evaluate(context))); + }; + }); + } + + private void registerItemAction() { + registerAction("item-amount", (args, chance) -> { + if (args instanceof Section section) { + boolean mainOrOff = section.getString("hand", "main").equalsIgnoreCase("main"); + int amount = section.getInt("amount", 1); + return context -> { + if (Math.random() > chance) return; + Player player = context.getHolder(); + ItemStack itemStack = mainOrOff ? player.getInventory().getItemInMainHand() : player.getInventory().getItemInOffHand(); + itemStack.setAmount(Math.max(0, itemStack.getAmount() + amount)); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at item-amount action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + }); + registerAction("durability", (args, chance) -> { + if (args instanceof Section section) { + EquipmentSlot slot = EquipmentSlot.valueOf(section.getString("slot", "hand").toUpperCase(Locale.ENGLISH)); + int amount = section.getInt("amount", 1); + return context -> { + if (Math.random() > chance) return; + Player player = context.getHolder(); + ItemStack itemStack = player.getInventory().getItem(slot); +// if (amount > 0) { +// ItemUtils.increaseDurability(itemStack, amount, true); +// } else { +// ItemUtils.decreaseDurability(context.getHolder(), itemStack, -amount, true); +// } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at durability action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + }); + registerAction("give-item", (args, chance) -> { + if (args instanceof Section section) { + String id = section.getString("item"); + int amount = section.getInt("amount", 1); + return context -> { + if (Math.random() > chance) return; + Player player = context.getHolder(); + ItemStack itemStack = plugin.getItemManager().buildAny(context, id); + int maxStack = itemStack.getType().getMaxStackSize(); + int amountToGive = amount; + while (amountToGive > 0) { + int perStackSize = Math.min(maxStack, amountToGive); + amountToGive -= perStackSize; + ItemStack more = itemStack.clone(); + more.setAmount(perStackSize); + PlayerUtils.dropItem(player, itemStack, true, true, false); + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at give-item action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + }); + } + + private void registerChainAction() { + registerAction("chain", (args, chance) -> { + List> actions = new ArrayList<>(); + if (args instanceof Section section) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + actions.add(parseAction(innerSection)); + } + } + } + return context -> { + if (Math.random() > chance) return; + for (Action action : actions) { + action.trigger(context); + } + }; + }); + registerAction("delay", (args, chance) -> { + List> actions = new ArrayList<>(); + int delay; + boolean async; + if (args instanceof Section section) { + delay = section.getInt("delay", 1); + async = section.getBoolean("async", false); + Section actionSection = section.getSection("actions"); + if (actionSection != null) + for (Map.Entry entry : actionSection.getStringRouteMappedValues(false).entrySet()) + if (entry.getValue() instanceof Section innerSection) + actions.add(parseAction(innerSection)); + } else { + delay = 1; + async = false; + } + return context -> { + if (Math.random() > chance) return; + Location location = context.arg(ContextKeys.LOCATION); + if (async) { + plugin.getScheduler().asyncLater(() -> { + for (Action action : actions) + action.trigger(context); + }, delay * 50L, TimeUnit.MILLISECONDS); + } else { + plugin.getScheduler().sync().runLater(() -> { + for (Action action : actions) + action.trigger(context); + }, delay, location); + } + }; + }); + registerAction("timer", (args, chance) -> { + List> actions = new ArrayList<>(); + int delay, duration, period; + boolean async; + if (args instanceof Section section) { + delay = section.getInt("delay", 2); + duration = section.getInt("duration", 20); + period = section.getInt("period", 2); + async = section.getBoolean("async", false); + Section actionSection = section.getSection("actions"); + if (actionSection != null) + for (Map.Entry entry : actionSection.getStringRouteMappedValues(false).entrySet()) + if (entry.getValue() instanceof Section innerSection) + actions.add(parseAction(innerSection)); + } else { + delay = 1; + period = 1; + async = false; + duration = 20; + } + return context -> { + if (Math.random() > chance) return; + Location location = context.arg(ContextKeys.LOCATION); + SchedulerTask task; + if (async) { + task = plugin.getScheduler().asyncRepeating(() -> { + for (Action action : actions) { + action.trigger(context); + } + }, delay * 50L, period * 50L, TimeUnit.MILLISECONDS); + } else { + task = plugin.getScheduler().sync().runRepeating(() -> { + for (Action action : actions) { + action.trigger(context); + } + }, delay, period, location); + } + plugin.getScheduler().asyncLater(task::cancel, duration * 50L, TimeUnit.MILLISECONDS); + }; + }); + registerAction("conditional", (args, chance) -> { + if (args instanceof Section section) { + Action[] actions = parseActions(section.getSection("actions")); + Requirement[] requirements = plugin.getRequirementManager().parseRequirements(section.getSection("conditions"), true); + return condition -> { + if (Math.random() > chance) return; + for (Requirement requirement : requirements) { + if (!requirement.isSatisfied(condition)) { + return; + } + } + for (Action action : actions) { + action.trigger(condition); + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at conditional action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + }); + registerAction("priority", (args, chance) -> { + if (args instanceof Section section) { + List[], Action[]>> conditionActionPairList = new ArrayList<>(); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + Action[] actions = parseActions(inner.getSection("actions")); + Requirement[] requirements = plugin.getRequirementManager().parseRequirements(inner.getSection("conditions"), false); + conditionActionPairList.add(Pair.of(requirements, actions)); + } + } + return context -> { + if (Math.random() > chance) return; + outer: + for (Pair[], Action[]> pair : conditionActionPairList) { + if (pair.left() != null) + for (Requirement requirement : pair.left()) { + if (!requirement.isSatisfied(context)) { + continue outer; + } + } + if (pair.right() != null) + for (Action action : pair.right()) { + action.trigger(context); + } + return; + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at priority action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + }); + } + + private void registerMoneyAction() { + registerAction("give-money", (args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (Math.random() > chance) return; + VaultHook.deposit(context.getHolder(), value.evaluate(context)); + }; + }); + registerAction("take-money", (args, chance) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (Math.random() > chance) return; + VaultHook.withdraw(context.getHolder(), value.evaluate(context)); + }; + }); + } + + private void registerPotionAction() { + registerAction("potion-effect", (args, chance) -> { + if (args instanceof Section section) { + PotionEffect potionEffect = new PotionEffect( + Objects.requireNonNull(PotionEffectType.getByName(section.getString("type", "BLINDNESS").toUpperCase(Locale.ENGLISH))), + section.getInt("duration", 20), + section.getInt("amplifier", 0) + ); + return context -> { + if (Math.random() > chance) return; + context.getHolder().addPotionEffect(potionEffect); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at potion-effect action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + }); + } + + private void registerSoundAction() { + registerAction("sound", (args, chance) -> { + if (args instanceof Section section) { + Sound sound = Sound.sound( + Key.key(section.getString("key")), + Sound.Source.valueOf(section.getString("source", "PLAYER").toUpperCase(Locale.ENGLISH)), + section.getDouble("volume", 1.0).floatValue(), + section.getDouble("pitch", 1.0).floatValue() + ); + return context -> { + if (Math.random() > chance) return; + Audience audience = plugin.getSenderFactory().getAudience(context.getHolder()); + AdventureHelper.playSound(audience, sound); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at sound action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + }); + } + + + private void registerPluginExpAction() { + registerAction("plugin-exp", (args, chance) -> { + if (args instanceof Section section) { + String pluginName = section.getString("plugin"); + MathValue value = MathValue.auto(section.get("exp")); + String target = section.getString("target"); + return context -> { + if (Math.random() > chance) return; + Optional.ofNullable(plugin.getIntegrationManager().getLevelerProvider(pluginName)).ifPresentOrElse(it -> { + it.addXp(context.getHolder(), target, value.evaluate(context)); + }, () -> plugin.getPluginLogger().warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation.")); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at plugin-exp action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + }); + } + + private void registerTitleAction() { + registerAction("title", (args, chance) -> { + if (args instanceof Section section) { + TextValue title = TextValue.auto(section.getString("title", "")); + TextValue subtitle = TextValue.auto(section.getString("subtitle", "")); + int fadeIn = section.getInt("fade-in", 20); + int stay = section.getInt("stay", 30); + int fadeOut = section.getInt("fade-out", 10); + return context -> { + if (Math.random() > chance) return; + final Player player = context.getHolder(); + Audience audience = plugin.getSenderFactory().getAudience(player); + AdventureHelper.sendTitle(audience, + AdventureHelper.miniMessage(title.render(context)), + AdventureHelper.miniMessage(subtitle.render(context)), + fadeIn, stay, fadeOut + ); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at title action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + }); + registerAction("random-title", (args, chance) -> { + if (args instanceof Section section) { + List titles = section.getStringList("titles"); + if (titles.isEmpty()) titles.add(""); + List subtitles = section.getStringList("subtitles"); + if (subtitles.isEmpty()) subtitles.add(""); + int fadeIn = section.getInt("fade-in", 20); + int stay = section.getInt("stay", 30); + int fadeOut = section.getInt("fade-out", 10); + return context -> { + if (Math.random() > chance) return; + TextValue title = TextValue.auto(titles.get(RandomUtils.generateRandomInt(0, titles.size() - 1))); + TextValue subtitle = TextValue.auto(subtitles.get(RandomUtils.generateRandomInt(0, subtitles.size() - 1))); + final Player player = context.getHolder(); + Audience audience = plugin.getSenderFactory().getAudience(player); + AdventureHelper.sendTitle(audience, + AdventureHelper.miniMessage(title.render(context)), + AdventureHelper.miniMessage(subtitle.render(context)), + fadeIn, stay, fadeOut + ); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at random-title action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + }); + registerAction("title-nearby", (args, chance) -> { + if (args instanceof Section section) { + TextValue title = TextValue.auto(section.getString("title")); + TextValue subtitle = TextValue.auto(section.getString("subtitle")); + int fadeIn = section.getInt("fade-in", 20); + int stay = section.getInt("stay", 30); + int fadeOut = section.getInt("fade-out", 10); + int range = section.getInt("range", 0); + return context -> { + if (Math.random() > chance) return; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + plugin.getScheduler().sync().run(() -> { + for (Entity player : location.getWorld().getNearbyEntities(location, range, range, range, entity -> entity instanceof Player)) { + double distance = LocationUtils.getDistance(player.getLocation(), location); + if (distance <= range) { + context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); + Audience audience = plugin.getSenderFactory().getAudience(player); + AdventureHelper.sendTitle(audience, + AdventureHelper.miniMessage(title.render(context)), + AdventureHelper.miniMessage(subtitle.render(context)), + fadeIn, stay, fadeOut + ); + } + } + }, location + ); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at title-nearby action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + }); + } + + private void registerFakeItemAction() { + registerAction("fake-item", ((args, chance) -> { + if (args instanceof Section section) { + String itemID = section.getString("item", ""); + String[] split = itemID.split(":"); + if (split.length >= 2) itemID = split[split.length - 1]; + MathValue duration = MathValue.auto(section.get("duration", 20)); + boolean position = !section.getString("position", "player").equals("player"); + MathValue x = MathValue.auto(section.get("x", 0)); + MathValue y = MathValue.auto(section.get("y", 0)); + MathValue z = MathValue.auto(section.get("z", 0)); + MathValue yaw = MathValue.auto(section.get("yaw", 0)); + int range = section.getInt("range", 0); + boolean opposite = section.getBoolean("opposite-yaw", false); + String finalItemID = itemID; + return context -> { + if (Math.random() > chance) return; + Player owner = context.getHolder(); + Location location = position ? requireNonNull(context.arg(ContextKeys.OTHER_LOCATION)).clone() : owner.getLocation().clone(); + location.add(x.evaluate(context), y.evaluate(context) - 1, z.evaluate(context)); + if (opposite) location.setYaw(-owner.getLocation().getYaw()); + else location.setYaw((float) yaw.evaluate(context)); + FakeArmorStand armorStand = SparrowHeart.getInstance().createFakeArmorStand(location); + armorStand.invisible(true); + armorStand.equipment(EquipmentSlot.HEAD, plugin.getItemManager().buildInternal(context, finalItemID)); + ArrayList viewers = new ArrayList<>(); + if (range > 0) { + for (Entity player : location.getWorld().getNearbyEntities(location, range, range, range, entity -> entity instanceof Player)) { + double distance = LocationUtils.getDistance(player.getLocation(), location); + if (distance <= range) { + viewers.add((Player) player); + } + } + } else { + viewers.add(owner); + } + for (Player player : viewers) { + armorStand.spawn(player); + } + plugin.getScheduler().asyncLater(() -> { + for (Player player : viewers) { + if (player.isOnline() && player.isValid()) { + armorStand.destroy(player); + } + } + }, (long) (duration.evaluate(context) * 50), TimeUnit.MILLISECONDS); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at fake-item action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + })); + } + + private void registerHologramAction() { + registerAction("hologram", ((args, chance) -> { + if (args instanceof Section section) { + TextValue text = TextValue.auto(section.getString("text", "")); + MathValue duration = MathValue.auto(section.get("duration", 20)); + boolean position = section.getString("position", "other").equals("other"); + MathValue x = MathValue.auto(section.get("x", 0)); + MathValue y = MathValue.auto(section.get("y", 0)); + MathValue z = MathValue.auto(section.get("z", 0)); + int range = section.getInt("range", 16); + return context -> { + if (Math.random() > chance) return; + Player owner = context.getHolder(); + Location location = position ? requireNonNull(context.arg(ContextKeys.OTHER_LOCATION)).clone() : owner.getLocation().clone(); + location.add(x.evaluate(context), y.evaluate(context), z.evaluate(context)); + FakeArmorStand armorStand = SparrowHeart.getInstance().createFakeArmorStand(location); + armorStand.invisible(true); + armorStand.small(true); + armorStand.name(AdventureHelper.miniMessageToJson(text.render(context))); + ArrayList viewers = new ArrayList<>(); + if (range > 0) { + for (Entity player : location.getWorld().getNearbyEntities(location, range, range, range, entity -> entity instanceof Player)) { + double distance = LocationUtils.getDistance(player.getLocation(), location); + if (distance <= range) { + viewers.add((Player) player); + } + } + } else { + viewers.add(owner); + } + for (Player player : viewers) { + armorStand.spawn(player); + } + plugin.getScheduler().asyncLater(() -> { + for (Player player : viewers) { + if (player.isOnline() && player.isValid()) { + armorStand.destroy(player); + } + } + }, (long) (duration.evaluate(context) * 50), TimeUnit.MILLISECONDS); + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at hologram action which is expected to be `Section`"); + return EmptyAction.INSTANCE; + } + })); + } + + private void registerFishFindAction() { + registerAction("fish-finder", (args, chance) -> { + String surrounding = (String) args; + return context -> { + if (Math.random() > chance) return; + String previous = context.arg(ContextKeys.SURROUNDING); + context.arg(ContextKeys.SURROUNDING, surrounding); + Collection loots = plugin.getLootManager().getWeightedLoots(Effect.newInstance(), context).keySet(); + StringJoiner stringJoiner = new StringJoiner(TranslationManager.miniMessageTranslation(MessageConstants.COMMAND_FISH_FINDER_SPLIT_CHAR.build().key())); + for (String loot : loots) { + plugin.getLootManager().getLoot(loot).ifPresent(lootIns -> { + if (lootIns.showInFinder()) { + if (!lootIns.nick().equals("UNDEFINED")) { + stringJoiner.add(lootIns.nick()); + } + } + }); + } + if (previous == null) { + context.remove(ContextKeys.SURROUNDING); + } else { + context.arg(ContextKeys.SURROUNDING, previous); + } + if (loots.isEmpty()) { + plugin.getSenderFactory().wrap(context.getHolder()).sendMessage(TranslationManager.render(MessageConstants.COMMAND_FISH_FINDER_NO_LOOT.build())); + } else { + plugin.getSenderFactory().wrap(context.getHolder()).sendMessage(TranslationManager.render(MessageConstants.COMMAND_FISH_FINDER_POSSIBLE_LOOTS.arguments(AdventureHelper.miniMessage(stringJoiner.toString())).build())); + } + }; + }); + } + + /** + * 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", "unchecked"}) + 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 = (Class>) ClassUtils.findClass(expansionJar, ActionExpansion.class); + classes.add(expansionClass); + } catch (IOException | ClassNotFoundException e) { + plugin.getPluginLogger().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()); + plugin.getPluginLogger().info("Loaded action expansion: " + expansion.getActionType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor() ); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + plugin.getPluginLogger().warn("Error occurred when creating expansion instance.", e); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/Languages.java b/core/src/main/java/net/momirealms/customfishing/bukkit/adventure/Languages.java similarity index 97% rename from plugin/src/main/java/net/momirealms/customfishing/adventure/component/Languages.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/adventure/Languages.java index ed3399d5..346c4879 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/Languages.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/adventure/Languages.java @@ -4,7 +4,7 @@ * Copyright (c) 2021 NichtStudioCode */ -package net.momirealms.customfishing.adventure.component; +package net.momirealms.customfishing.bukkit.adventure; import com.google.gson.stream.JsonReader; import org.bukkit.entity.Player; diff --git a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentUtils.java b/core/src/main/java/net/momirealms/customfishing/bukkit/adventure/ShadedAdventureComponentUtils.java similarity index 94% rename from plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentUtils.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/adventure/ShadedAdventureComponentUtils.java index 9709b287..233c8b4e 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentUtils.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/adventure/ShadedAdventureComponentUtils.java @@ -4,7 +4,7 @@ * Copyright (c) 2021 NichtStudioCode */ -package net.momirealms.customfishing.adventure.component; +package net.momirealms.customfishing.bukkit.adventure; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; diff --git a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentWrapper.java b/core/src/main/java/net/momirealms/customfishing/bukkit/adventure/ShadedAdventureComponentWrapper.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentWrapper.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/adventure/ShadedAdventureComponentWrapper.java index 46e56122..75832f79 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureComponentWrapper.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/adventure/ShadedAdventureComponentWrapper.java @@ -4,7 +4,7 @@ * Copyright (c) 2021 NichtStudioCode */ -package net.momirealms.customfishing.adventure.component; +package net.momirealms.customfishing.bukkit.adventure; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; diff --git a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureShadedComponentLocalizer.java b/core/src/main/java/net/momirealms/customfishing/bukkit/adventure/ShadedAdventureShadedComponentLocalizer.java similarity index 97% rename from plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureShadedComponentLocalizer.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/adventure/ShadedAdventureShadedComponentLocalizer.java index 5fcebf65..89c242ef 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedAdventureShadedComponentLocalizer.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/adventure/ShadedAdventureShadedComponentLocalizer.java @@ -4,7 +4,7 @@ * Copyright (c) 2021 NichtStudioCode */ -package net.momirealms.customfishing.adventure.component; +package net.momirealms.customfishing.bukkit.adventure; import net.kyori.adventure.text.*; diff --git a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedComponentLocalizer.java b/core/src/main/java/net/momirealms/customfishing/bukkit/adventure/ShadedComponentLocalizer.java similarity index 97% rename from plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedComponentLocalizer.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/adventure/ShadedComponentLocalizer.java index 418c7d94..7e125fba 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/adventure/component/ShadedComponentLocalizer.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/adventure/ShadedComponentLocalizer.java @@ -4,7 +4,7 @@ * Copyright (c) 2021 NichtStudioCode */ -package net.momirealms.customfishing.adventure.component; +package net.momirealms.customfishing.bukkit.adventure; import java.util.ArrayList; import java.util.List; diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/bag/BukkitBagManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/bag/BukkitBagManager.java new file mode 100644 index 00000000..bd2e53fa --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/bag/BukkitBagManager.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.bag; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.event.FishingBagPreCollectEvent; +import net.momirealms.customfishing.api.event.FishingLootSpawnEvent; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.action.ActionManager; +import net.momirealms.customfishing.api.mechanic.bag.BagManager; +import net.momirealms.customfishing.api.mechanic.bag.FishingBagHolder; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.requirement.Requirement; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager; +import net.momirealms.customfishing.api.storage.user.UserData; +import net.momirealms.customfishing.api.util.EventUtils; +import net.momirealms.customfishing.bukkit.config.BukkitConfigManager; +import net.momirealms.customfishing.bukkit.util.PlayerUtils; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import net.momirealms.sparrow.heart.SparrowHeart; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +public class BukkitBagManager implements BagManager, Listener { + + private final BukkitCustomFishingPlugin plugin; + private final HashMap tempEditMap; + private Action[] collectLootActions; + private Action[] bagFullActions; + private boolean bagStoreLoots; + private boolean bagStoreRods; + private boolean bagStoreBaits; + private boolean bagStoreHooks; + private boolean bagStoreUtils; + private final HashSet storedTypes = new HashSet<>(); + private boolean enable; + private String bagTitle; + private List bagWhiteListItems = new ArrayList<>(); + private Requirement[] collectRequirements; + + public BukkitBagManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + this.tempEditMap = new HashMap<>(); + } + + @Override + public void load() { + this.loadConfig(); + Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap()); + } + + @Override + public void unload() { + HandlerList.unregisterAll(this); + storedTypes.clear(); + } + + @Override + public void disable() { + unload(); + this.plugin.getStorageManager().getDataSource().updateManyPlayersData(tempEditMap.values(), true); + } + + @EventHandler + public void onLootSpawn(FishingLootSpawnEvent event) { + if (!enable || !bagStoreLoots) { + return; + } + if (!event.summonEntity()) { + return; + } + if (!(event.getEntity() instanceof Item itemEntity)) { + return; + } + + Player player = event.getPlayer(); + Context context = event.getContext(); + if (!RequirementManager.isSatisfied(context, collectRequirements)) { + return; + } + + Optional onlineUser = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); + if (onlineUser.isEmpty()) { + return; + } + UserData userData = onlineUser.get(); + Inventory inventory = userData.holder().getInventory(); + ItemStack item = itemEntity.getItemStack(); + FishingBagPreCollectEvent preCollectEvent = new FishingBagPreCollectEvent(player, item, inventory); + if (EventUtils.fireAndCheckCancel(preCollectEvent)) { + return; + } + + int cannotPut = PlayerUtils.putItemsToInventory(inventory, item, item.getAmount()); + // some are put into bag + if (cannotPut != item.getAmount()) { + ActionManager.trigger(context, collectLootActions); + } + // all are put + if (cannotPut == 0) { + event.summonEntity(false); + return; + } + item.setAmount(cannotPut); + itemEntity.setItemStack(item); + ActionManager.trigger(context, bagFullActions); + } + + private void loadConfig() { + Section config = BukkitConfigManager.getMainConfig().getSection("mechanics.fishing-bag"); + + enable = config.getBoolean("enable", true); + bagTitle = config.getString("bag-title", ""); + bagStoreLoots = config.getBoolean("can-store-loot", false); + bagStoreRods = config.getBoolean("can-store-rod", true); + bagStoreBaits = config.getBoolean("can-store-bait", true); + bagStoreHooks = config.getBoolean("can-store-hook", true); + bagStoreUtils = config.getBoolean("can-store-util", true); + bagWhiteListItems = config.getStringList("whitelist-items").stream().map(it -> Material.valueOf(it.toUpperCase(Locale.ENGLISH))).toList(); + collectLootActions = plugin.getActionManager().parseActions(config.getSection("collect-actions")); + bagFullActions = plugin.getActionManager().parseActions(config.getSection("full-actions")); + collectRequirements = plugin.getRequirementManager().parseRequirements(config.getSection("collect-requirements"), false); + + if (bagStoreLoots) storedTypes.add(MechanicType.LOOT); + if (bagStoreRods) storedTypes.add(MechanicType.ROD); + if (bagStoreBaits) storedTypes.add(MechanicType.BAIT); + if (bagStoreHooks) storedTypes.add(MechanicType.HOOK); + if (bagStoreUtils) storedTypes.add(MechanicType.UTIL); + } + + @Override + public CompletableFuture openBag(Player viewer, UUID owner) { + CompletableFuture future = new CompletableFuture<>(); + if (enable) { + Optional onlineUser = plugin.getStorageManager().getOnlineUser(owner); + onlineUser.ifPresentOrElse(data -> { + viewer.openInventory(data.holder().getInventory()); + SparrowHeart.getInstance().updateInventoryTitle(viewer, AdventureHelper.componentToJson(AdventureHelper.miniMessage(plugin.getPlaceholderManager().parse(Bukkit.getOfflinePlayer(owner), bagTitle, Map.of("{uuid}", owner.toString(), "{player}", data.name()))))); + future.complete(true); + }, () -> plugin.getStorageManager().getOfflineUserData(owner, true).thenAccept(result -> result.ifPresentOrElse(data -> { + if (data.isLocked()) { + future.completeExceptionally(new RuntimeException("Data is locked")); + return; + } + this.tempEditMap.put(viewer.getUniqueId(), data); + viewer.openInventory(data.holder().getInventory()); + SparrowHeart.getInstance().updateInventoryTitle(viewer, AdventureHelper.componentToJson(AdventureHelper.miniMessage(plugin.getPlaceholderManager().parse(Bukkit.getOfflinePlayer(owner), bagTitle, Map.of("{uuid}", owner.toString(), "{player}", data.name()))))); + future.complete(true); + }, () -> future.complete(false)))); + } else { + future.complete(false); + } + return future; + } + + /** + * 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)) + return; + final Player viewer = (Player) event.getPlayer(); + UserData userData = tempEditMap.remove(viewer.getUniqueId()); + if (userData == null) + return; + this.plugin.getStorageManager().saveUserData(userData, 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 (ignoreCancelled = true) + public void onInvClick(InventoryClickEvent event) { + if (!(event.getInventory().getHolder() instanceof FishingBagHolder)) + return; + ItemStack movedItem = event.getCurrentItem(); + Inventory clicked = event.getClickedInventory(); + if (clicked != event.getWhoClicked().getInventory()) { + if (event.getAction() != InventoryAction.HOTBAR_SWAP && event.getAction() != InventoryAction.HOTBAR_MOVE_AND_READD) { + return; + } + movedItem = event.getWhoClicked().getInventory().getItem(event.getHotbarButton()); + } + if (movedItem == null || movedItem.getType() == Material.AIR || bagWhiteListItems.contains(movedItem.getType())) + return; + String id = plugin.getItemManager().getItemID(movedItem); + List type = MechanicType.getTypeByID(id); + if (type == null) { + event.setCancelled(true); + return; + } + for (MechanicType mechanicType : type) { + if (storedTypes.contains(mechanicType)) { + 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) { + UserData userData = tempEditMap.remove(event.getPlayer().getUniqueId()); + if (userData == null) + return; + plugin.getStorageManager().saveUserData(userData, true); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/block/BukkitBlockManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/block/BukkitBlockManager.java new file mode 100644 index 00000000..5aa8a20c --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/block/BukkitBlockManager.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.block; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.integration.BlockProvider; +import net.momirealms.customfishing.api.integration.ExternalProvider; +import net.momirealms.customfishing.api.mechanic.block.*; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import net.momirealms.customfishing.common.util.Pair; +import net.momirealms.customfishing.common.util.RandomUtils; +import net.momirealms.customfishing.common.util.Tuple; +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.Container; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Directional; +import org.bukkit.block.data.Rotatable; +import org.bukkit.block.data.type.NoteBlock; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.FallingBlock; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityChangeBlockEvent; +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.util.*; +import java.util.concurrent.ThreadLocalRandom; + +import static java.util.Objects.requireNonNull; + +public class BukkitBlockManager implements BlockManager, Listener { + + private final BukkitCustomFishingPlugin plugin; + private final HashMap blockProviders = new HashMap<>(); + private final HashMap blocks = new HashMap<>(); + private final HashMap dataFactories = new HashMap<>(); + private final HashMap stateFactories = new HashMap<>(); + private BlockProvider[] blockDetectArray; + + public BukkitBlockManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + this.registerInbuiltProperties(); + this.registerBlockProvider(new BlockProvider() { + @Override + public String identifier() { + return "vanilla"; + } + @Override + public BlockData blockData(@NotNull Context context, @NotNull String id, List modifiers) { + BlockData blockData = Material.valueOf(id.toUpperCase(Locale.ENGLISH)).createBlockData(); + for (BlockDataModifier modifier : modifiers) + modifier.apply(context, blockData); + return blockData; + } + @NotNull + @Override + public String blockID(@NotNull Block block) { + return block.getType().name(); + } + }); + } + + @Override + public void load() { + Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap()); + this.resetBlockDetectionOrder(); + for (BlockProvider provider : blockProviders.values()) { + plugin.debug("Registered BlockProvider: " + provider.identifier()); + } + plugin.debug("Loaded " + blocks.size() + " blocks"); + plugin.debug("Block order: " + Arrays.toString(Arrays.stream(blockDetectArray).map(ExternalProvider::identifier).toList().toArray(new String[0]))); + } + + @Override + public void unload() { + HandlerList.unregisterAll(this); + this.blocks.clear(); + } + + @Override + public void disable() { + this.blockProviders.clear(); + } + + private void resetBlockDetectionOrder() { + ArrayList list = new ArrayList<>(); + for (String plugin : ConfigManager.blockDetectOrder()) { + BlockProvider library = blockProviders.get(plugin); + if (library != null) + list.add(library); + } + this.blockDetectArray = list.toArray(new BlockProvider[0]); + } + + @Override + public boolean registerBlock(@NotNull BlockConfig block) { + if (blocks.containsKey(block.id())) return false; + blocks.put(block.id(), block); + return true; + } + + /** + * 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; + + // Retrieve a custom string value stored in the entity's persistent data container. + String temp = event.getEntity().getPersistentDataContainer().get( + requireNonNull(NamespacedKey.fromString("block", plugin.getBoostrap())), + 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= blocks.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(); + event.getBlock().setType(Material.AIR); + return; + } + + Context context = Context.player(player); + Location location = event.getBlock().getLocation(); + + // Apply block state modifiers from the BlockConfig to the block 1 tick later. + plugin.getScheduler().sync().runLater(() -> { + BlockState state = location.getBlock().getState(); + for (BlockStateModifier modifier : blockConfig.stateModifiers()) { + modifier.apply(context, state); + } + }, 1, location); + } + + public boolean registerBlockProvider(BlockProvider blockProvider) { + if (this.blockProviders.containsKey(blockProvider.identifier())) return false; + this.blockProviders.put(blockProvider.identifier(), blockProvider); + this.resetBlockDetectionOrder(); + return true; + } + + public boolean unregisterBlockProvider(String identification) { + boolean success = blockProviders.remove(identification) != null; + if (success) + this.resetBlockDetectionOrder(); + return success; + } + + public boolean registerBlockDataModifierBuilder(String type, BlockDataModifierFactory factory) { + if (this.dataFactories.containsKey(type)) return false; + this.dataFactories.put(type, factory); + return true; + } + + public boolean registerBlockStateModifierBuilder(String type, BlockStateModifierFactory factory) { + if (stateFactories.containsKey(type)) return false; + this.stateFactories.put(type, factory); + return true; + } + + public boolean unregisterBlockDataModifierBuilder(String type) { + return this.dataFactories.remove(type) != null; + } + + public boolean unregisterBlockStateModifierBuilder(String type) { + return this.stateFactories.remove(type) != null; + } + + private void registerInbuiltProperties() { + this.registerDirectional(); + this.registerStorage(); + this.registerRotatable(); + this.registerNoteBlock(); + } + + @Override + @NotNull + public FallingBlock summonBlockLoot(@NotNull Context context) { + String id = context.arg(ContextKeys.ID); + BlockConfig config = requireNonNull(blocks.get(id), "Block " + id + " not found"); + String blockID = config.blockID(); + BlockData blockData; + if (blockID.contains(":")) { + String[] split = blockID.split(":", 2); + BlockProvider provider = requireNonNull(blockProviders.get(split[0]), "BlockProvider " + split[0] + " doesn't exist"); + blockData = requireNonNull(provider.blockData(context, split[1], config.dataModifier()), "Block " + split[1] + " doesn't exist"); + } else { + blockData = blockProviders.get("vanilla").blockData(context, blockID, config.dataModifier()); + } + Location hookLocation = requireNonNull(context.arg(ContextKeys.OTHER_LOCATION)); + Location playerLocation = requireNonNull(context.getHolder()).getLocation(); + FallingBlock fallingBlock = hookLocation.getWorld().spawn(hookLocation, FallingBlock.class, (fb -> fb.setBlockData(blockData))); + fallingBlock.getPersistentDataContainer().set( + requireNonNull(NamespacedKey.fromString("block", plugin.getBoostrap())), + PersistentDataType.STRING, + id + ";" + context.getHolder().getName() + ); + Vector vector = playerLocation.subtract(hookLocation).toVector().multiply(1.2 - 1); + vector = vector.setY((vector.getY() + 0.2) * 1.2); + fallingBlock.setVelocity(vector); + return fallingBlock; + } + + @Override + @NotNull + public String getBlockID(@NotNull Block block) { + for (BlockProvider blockProvider : blockDetectArray) { + String id = blockProvider.blockID(block); + if (id != null) return id; + } + // Should not reach this because vanilla library would always work + return "AIR"; + } + + private void registerDirectional() { + this.registerBlockDataModifierBuilder("directional-4", (args) -> (context, blockData) -> { + boolean arg = (boolean) args; + if (arg && blockData instanceof Directional directional) { + directional.setFacing(BlockFace.values()[ThreadLocalRandom.current().nextInt(0, 4)]); + } + }); + this.registerBlockDataModifierBuilder("directional-6", (args) -> (context, blockData) -> { + boolean arg = (boolean) args; + if (arg && blockData instanceof Directional directional) { + directional.setFacing(BlockFace.values()[ThreadLocalRandom.current().nextInt(0, 6)]); + } + }); + } + + private void registerRotatable() { + this.registerBlockDataModifierBuilder("rotatable", (args) -> { + boolean arg = (boolean) args; + return (context, blockData) -> { + if (arg && blockData instanceof Rotatable rotatable) { + rotatable.setRotation(BlockFace.values()[ThreadLocalRandom.current().nextInt(BlockFace.values().length)]); + } + }; + }); + } + + private void registerNoteBlock() { + this.registerBlockDataModifierBuilder("noteblock", (args) -> { + if (args instanceof Section section) { + var instrument = Instrument.valueOf(section.getString("instrument")); + var note = new Note(section.getInt("note")); + return (context, blockData) -> { + if (blockData instanceof NoteBlock noteBlock) { + noteBlock.setNote(note); + noteBlock.setInstrument(instrument); + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at noteblock property which should be Section"); + return EmptyBlockDataModifier.INSTANCE; + } + }); + } + + private void registerStorage() { + this.registerBlockStateModifierBuilder("storage", (args) -> { + if (args instanceof Section section) { + List, String, Pair, MathValue>>> contents = new ArrayList<>(); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection inner) { + String item = inner.getString("item"); + String[] split = inner.getString("amount","1~1").split("~"); + Pair, MathValue> amountPair = Pair.of(MathValue.auto(split[0]), MathValue.auto(split[1])); + MathValue chance = MathValue.auto(inner.get("chance", 1d)); + contents.add(Tuple.of(chance, item, amountPair)); + } + } + return (context, blockState) -> { + if (blockState instanceof Container container) { + setInventoryItems(contents, context, container.getInventory()); + } + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at storage property which should be Section"); + return EmptyBlockStateModifier.INSTANCE; + } + }); + } + + private void setInventoryItems( + List, String, Pair, MathValue>>> contents, + Context context, + Inventory inventory + ) { + LinkedList unused = new LinkedList<>(); + for (int i = 0; i < inventory.getSize(); i++) { + unused.add(i); + } + Collections.shuffle(unused); + for (Tuple, String, Pair, MathValue>> tuple : contents) { + if (tuple.left().evaluate(context) > Math.random()) { + ItemStack itemStack = plugin.getItemManager().buildAny(context, tuple.mid()); + if (itemStack != null) { + itemStack.setAmount(RandomUtils.generateRandomInt((int) tuple.right().left().evaluate(context), (int) (tuple.right().right().evaluate(context)))); + inventory.setItem(unused.pop(), itemStack); + } + } + } + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/BukkitCommandFeature.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/BukkitCommandFeature.java new file mode 100644 index 00000000..62bd90b0 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/BukkitCommandFeature.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.common.command.AbstractCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.sender.SenderFactory; +import net.momirealms.customfishing.common.util.Pair; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.incendo.cloud.bukkit.data.Selector; + +import java.util.Collection; + +public abstract class BukkitCommandFeature extends AbstractCommandFeature { + + public BukkitCommandFeature(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + @SuppressWarnings("unchecked") + protected SenderFactory getSenderFactory() { + return (SenderFactory) BukkitCustomFishingPlugin.getInstance().getSenderFactory(); + } + + public Pair resolveSelector(Selector selector, TranslatableComponent.Builder single, TranslatableComponent.Builder multiple) { + Collection entities = selector.values(); + if (entities.size() == 1) { + return Pair.of(single, Component.text(entities.iterator().next().getName())); + } else { + return Pair.of(multiple, Component.text(entities.size())); + } + } + + public Pair resolveSelector(Collection selector, TranslatableComponent.Builder single, TranslatableComponent.Builder multiple) { + if (selector.size() == 1) { + return Pair.of(single, Component.text(selector.iterator().next().getName())); + } else { + return Pair.of(multiple, Component.text(selector.size())); + } + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/BukkitCommandManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/BukkitCommandManager.java new file mode 100644 index 00000000..b62ad251 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/BukkitCommandManager.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command; + +import net.kyori.adventure.util.Index; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.command.feature.*; +import net.momirealms.customfishing.common.command.AbstractCommandManager; +import net.momirealms.customfishing.common.command.CommandFeature; +import net.momirealms.customfishing.common.sender.Sender; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.SenderMapper; +import org.incendo.cloud.bukkit.CloudBukkitCapabilities; +import org.incendo.cloud.execution.ExecutionCoordinator; +import org.incendo.cloud.paper.LegacyPaperCommandManager; +import org.incendo.cloud.setting.ManagerSetting; + +import java.util.List; + +public class BukkitCommandManager extends AbstractCommandManager { + + private final List> FEATURES = List.of( + new ReloadCommand(this), + new SellFishCommand(this), + new GetItemCommand(this), + new GiveItemCommand(this), + new ImportItemCommand(this), + new EndCompetitionCommand(this), + new StopCompetitionCommand(this), + new StartCompetitionCommand(this), + new OpenMarketCommand(this), + new OpenBagCommand(this), + new FishingBagCommand(this), + new EditOnlineBagCommand(this), + new EditOfflineBagCommand(this), + new UnlockDataCommand(this), + new ImportDataCommand(this), + new ExportDataCommand(this), + new AddStatisticsCommand(this), + new SetStatisticsCommand(this), + new ResetStatisticsCommand(this), + new QueryStatisticsCommand(this) + ); + + private final Index> INDEX = Index.create(CommandFeature::getFeatureID, FEATURES); + + public BukkitCommandManager(BukkitCustomFishingPlugin plugin) { + super(plugin, new LegacyPaperCommandManager<>( + plugin.getBoostrap(), + ExecutionCoordinator.simpleCoordinator(), + SenderMapper.identity() + )); + final LegacyPaperCommandManager manager = (LegacyPaperCommandManager) getCommandManager(); + manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true); + if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) { + manager.registerBrigadier(); + manager.brigadierManager().setNativeNumberSuggestions(true); + } else if (manager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) { + manager.registerAsynchronousCompletions(); + } + } + + @Override + protected Sender wrapSender(CommandSender sender) { + return ((BukkitCustomFishingPlugin) plugin).getSenderFactory().wrap(sender); + } + + @Override + public Index> getFeatures() { + return INDEX; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/AddStatisticsCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/AddStatisticsCommand.java new file mode 100644 index 00000000..18b60da1 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/AddStatisticsCommand.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.statistic.FishingStatistics; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; +import org.incendo.cloud.parser.standard.DoubleParser; +import org.incendo.cloud.parser.standard.EnumParser; +import org.incendo.cloud.parser.standard.StringParser; + +public class AddStatisticsCommand extends BukkitCommandFeature { + + public AddStatisticsCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .flag(manager.flagBuilder("silent").withAliases("s")) + .required("player", PlayerParser.playerParser()) + .required("id", StringParser.stringParser()) + .required("type", EnumParser.enumParser(FishingStatistics.Type.class)) + .required("value", DoubleParser.doubleParser(0)) + .handler(context -> { + Player player = context.get("player"); + String id = context.get("id"); + FishingStatistics.Type type = context.get("type"); + double value = context.get("value"); + BukkitCustomFishingPlugin.getInstance().getStorageManager().getOnlineUser(player.getUniqueId()).ifPresentOrElse(userData -> { + if (type == FishingStatistics.Type.AMOUNT_OF_FISH_CAUGHT) { + userData.statistics().addAmount(id, (int) value); + handleFeedback(context, MessageConstants.COMMAND_STATISTICS_MODIFY_SUCCESS, Component.text(player.getName())); + } else if (type == FishingStatistics.Type.MAX_SIZE) { + handleFeedback(context, MessageConstants.COMMAND_STATISTICS_FAILURE_UNSUPPORTED); + } + }, () -> handleFeedback(context, MessageConstants.COMMAND_STATISTICS_FAILURE_NOT_LOADED)); + }); + } + + @Override + public String getFeatureID() { + return "statistics_add"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/EditOfflineBagCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/EditOfflineBagCommand.java new file mode 100644 index 00000000..b6488b62 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/EditOfflineBagCommand.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.parser.standard.UUIDParser; + +import java.util.UUID; + +public class EditOfflineBagCommand extends BukkitCommandFeature { + + public EditOfflineBagCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .required("uuid", UUIDParser.uuidParser()) + .handler(context -> { + Player admin = context.sender(); + UUID uuid = context.get("uuid"); + BukkitCustomFishingPlugin.getInstance().getBagManager().openBag(admin, uuid).whenComplete((result, throwable) -> { + if (throwable != null) { + handleFeedback(context, MessageConstants.COMMAND_BAG_EDIT_FAILURE_UNSAFE); + return; + } + if (!result) { + handleFeedback(context, MessageConstants.COMMAND_BAG_EDIT_FAILURE_NEVER_PLAYED); + return; + } + }); + }); + } + + @Override + public String getFeatureID() { + return "edit_offline_bag"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/EditOnlineBagCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/EditOnlineBagCommand.java new file mode 100644 index 00000000..043f58a9 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/EditOnlineBagCommand.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; + +public class EditOnlineBagCommand extends BukkitCommandFeature { + + public EditOnlineBagCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .required("player", PlayerParser.playerParser()) + .handler(context -> { + Player admin = context.sender(); + Player online = context.get("player"); + BukkitCustomFishingPlugin.getInstance().getBagManager().openBag(admin, online.getUniqueId()); + }); + } + + @Override + public String getFeatureID() { + return "edit_online_bag"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/EndCompetitionCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/EndCompetitionCommand.java new file mode 100644 index 00000000..7cd6b068 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/EndCompetitionCommand.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public class EndCompetitionCommand extends BukkitCommandFeature { + + public EndCompetitionCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .flag(manager.flagBuilder("silent").withAliases("s").build()) + .handler(context -> { + FishingCompetition competition = BukkitCustomFishingPlugin.getInstance().getCompetitionManager().getOnGoingCompetition(); + if (competition == null) { + handleFeedback(context, MessageConstants.COMMAND_COMPETITION_FAILURE_NO_COMPETITION); + } else { + competition.end(true); + handleFeedback(context, MessageConstants.COMMAND_COMPETITION_END_SUCCESS); + } + }); + } + + @Override + public String getFeatureID() { + return "end_competition"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ExportDataCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ExportDataCommand.java new file mode 100644 index 00000000..a14dac23 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ExportDataCommand.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.storage.DataStorageProvider; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import net.momirealms.customfishing.common.util.CompletableFutures; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.GZIPOutputStream; + +public class ExportDataCommand extends BukkitCommandFeature { + + public ExportDataCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(ConsoleCommandSender.class) + .flag(manager.flagBuilder("silent").withAliases("s").build()) + .handler(context -> { + if (!Bukkit.getOnlinePlayers().isEmpty()) { + handleFeedback(context, MessageConstants.COMMAND_DATA_EXPORT_FAILURE_PLAYER_ONLINE); + return; + } + + BukkitCustomFishingPlugin plugin = BukkitCustomFishingPlugin.getInstance(); + handleFeedback(context, MessageConstants.COMMAND_DATA_EXPORT_START); + plugin.getScheduler().async().execute(() -> { + + DataStorageProvider storageProvider = plugin.getStorageManager().getDataSource(); + + Set uuids = storageProvider.getUniqueUsers(); + Set> futures = new HashSet<>(); + AtomicInteger userCount = new AtomicInteger(0); + Map out = Collections.synchronizedMap(new TreeMap<>()); + + int amount = uuids.size(); + for (UUID uuid : uuids) { + futures.add(storageProvider.getPlayerData(uuid, false).thenAccept(it -> { + if (it.isPresent()) { + out.put(uuid, plugin.getStorageManager().toJson(it.get())); + userCount.incrementAndGet(); + } + })); + } + + CompletableFuture overallFuture = CompletableFutures.allOf(futures); + + while (true) { + try { + overallFuture.get(3, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + break; + } catch (TimeoutException e) { + handleFeedback(context, MessageConstants.COMMAND_DATA_EXPORT_PROGRESS, Component.text(userCount.get()), Component.text(amount)); + continue; + } + break; + } + + JsonObject outJson = new JsonObject(); + for (Map.Entry entry : out.entrySet()) { + outJson.addProperty(entry.getKey().toString(), entry.getValue()); + } + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm"); + String formattedDate = formatter.format(new Date()); + File outFile = new File(plugin.getDataFolder(), "exported-" + formattedDate + ".json.gz"); + try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(outFile.toPath())), StandardCharsets.UTF_8))) { + new GsonBuilder().disableHtmlEscaping().create().toJson(outJson, writer); + } catch (IOException e) { + throw new RuntimeException("Unexpected issue: ", e); + } + + handleFeedback(context, MessageConstants.COMMAND_DATA_EXPORT_SUCCESS); + }); + }); + } + + @Override + public String getFeatureID() { + return "data_export"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/FishingBagCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/FishingBagCommand.java new file mode 100644 index 00000000..1300f28f --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/FishingBagCommand.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public class FishingBagCommand extends BukkitCommandFeature { + + public FishingBagCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .handler(context -> { + BukkitCustomFishingPlugin.getInstance().getBagManager().openBag(context.sender(), context.sender().getUniqueId()).whenComplete((result, e) -> { + if (!result || e != null) { + handleFeedback(context, MessageConstants.COMMAND_DATA_FAILURE_NOT_LOADED); + } + }); + }); + } + + @Override + public String getFeatureID() { + return "fishingbag"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/GetItemCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/GetItemCommand.java new file mode 100644 index 00000000..a88f066c --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/GetItemCommand.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.bukkit.util.PlayerUtils; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.IntegerParser; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.concurrent.CompletableFuture; + +@SuppressWarnings("DuplicatedCode") +public class GetItemCommand extends BukkitCommandFeature { + + public GetItemCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .required("id", StringParser.stringComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + return CompletableFuture.completedFuture(BukkitCustomFishingPlugin.getInstance().getItemManager().getItemIDs().stream().map(Suggestion::suggestion).toList()); + } + })) + .optional("amount", IntegerParser.integerParser(1, 6400)) + .flag(manager.flagBuilder("silent").withAliases("s").build()) + .handler(context -> { + final int amount = context.getOrDefault("amount", 1); + final String id = context.get("id"); + final Player player = context.sender(); + try { + ItemStack itemStack = BukkitCustomFishingPlugin.getInstance().getItemManager().buildInternal(Context.player(player).arg(ContextKeys.ID, id), id); + if (itemStack == null) { + throw new RuntimeException("Unrecognized item id: " + id); + } + int amountToGive = amount; + int maxStack = itemStack.getType().getMaxStackSize(); + while (amountToGive > 0) { + int perStackSize = Math.min(maxStack, amountToGive); + amountToGive -= perStackSize; + ItemStack more = itemStack.clone(); + more.setAmount(perStackSize); + PlayerUtils.dropItem(player, more, false, true, false); + } + handleFeedback(context, MessageConstants.COMMAND_ITEM_GET_SUCCESS, Component.text(amount), Component.text(id)); + } catch (NullPointerException e) { + handleFeedback(context, MessageConstants.COMMAND_ITEM_FAILURE_NOT_EXIST, Component.text(id)); + } + }); + } + + @Override + public String getFeatureID() { + return "get_item"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/GiveItemCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/GiveItemCommand.java new file mode 100644 index 00000000..8e2dc5a3 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/GiveItemCommand.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.bukkit.util.PlayerUtils; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.IntegerParser; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.concurrent.CompletableFuture; + +@SuppressWarnings("DuplicatedCode") +public class GiveItemCommand extends BukkitCommandFeature { + + public GiveItemCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("player", PlayerParser.playerParser()) + .required("id", StringParser.stringComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + return CompletableFuture.completedFuture(BukkitCustomFishingPlugin.getInstance().getItemManager().getItemIDs().stream().map(Suggestion::suggestion).toList()); + } + })) + .optional("amount", IntegerParser.integerParser(1, 6400)) + .flag(manager.flagBuilder("silent").withAliases("s").build()) + .handler(context -> { + final Player player = context.get("player"); + final int amount = context.getOrDefault("amount", 1); + final String id = context.get("id"); + try { + ItemStack itemStack = BukkitCustomFishingPlugin.getInstance().getItemManager().buildInternal(Context.player(player).arg(ContextKeys.ID, id), id); + if (itemStack == null) { + throw new RuntimeException("Unrecognized item id: " + id); + } + int amountToGive = amount; + int maxStack = itemStack.getType().getMaxStackSize(); + while (amountToGive > 0) { + int perStackSize = Math.min(maxStack, amountToGive); + amountToGive -= perStackSize; + ItemStack more = itemStack.clone(); + more.setAmount(perStackSize); + PlayerUtils.dropItem(player, more, false, true, false); + } + handleFeedback(context, MessageConstants.COMMAND_ITEM_GIVE_SUCCESS, Component.text(player.getName()), Component.text(amount), Component.text(id)); + } catch (NullPointerException e) { + handleFeedback(context, MessageConstants.COMMAND_ITEM_FAILURE_NOT_EXIST, Component.text(id)); + } + }); + } + + @Override + public String getFeatureID() { + return "give_item"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ImportDataCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ImportDataCommand.java new file mode 100644 index 00000000..0f3405eb --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ImportDataCommand.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.storage.DataStorageProvider; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import net.momirealms.customfishing.common.util.CompletableFutures; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.parser.standard.StringParser; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.GZIPInputStream; + +public class ImportDataCommand extends BukkitCommandFeature { + + public ImportDataCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(ConsoleCommandSender.class) + .flag(manager.flagBuilder("silent").withAliases("s").build()) + .required("file", StringParser.greedyFlagYieldingStringParser()) + .handler(context -> { + if (!Bukkit.getOnlinePlayers().isEmpty()) { + handleFeedback(context, MessageConstants.COMMAND_DATA_IMPORT_FAILURE_PLAYER_ONLINE); + return; + } + String fileName = context.get("file"); + BukkitCustomFishingPlugin plugin = BukkitCustomFishingPlugin.getInstance(); + File file = new File(plugin.getDataFolder(), fileName); + if (!file.exists()) { + handleFeedback(context, MessageConstants.COMMAND_DATA_IMPORT_FAILURE_NOT_EXISTS); + return; + } + if (!file.getName().endsWith(".json.gz")) { + handleFeedback(context, MessageConstants.COMMAND_DATA_IMPORT_FAILURE_INVALID_FILE); + return; + } + + handleFeedback(context, MessageConstants.COMMAND_DATA_IMPORT_START); + plugin.getScheduler().async().execute(() -> { + + JsonObject data; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(file.toPath())), StandardCharsets.UTF_8))) { + data = new GsonBuilder().disableHtmlEscaping().create().fromJson(reader, JsonObject.class); + } catch (IOException e) { + throw new RuntimeException("Unexpected issue: ", e); + } + + DataStorageProvider storageProvider = plugin.getStorageManager().getDataSource(); + var entrySet = data.entrySet(); + int amount = entrySet.size(); + AtomicInteger userCount = new AtomicInteger(0); + Set> futures = new HashSet<>(); + + for (Map.Entry entry : entrySet) { + UUID uuid = UUID.fromString(entry.getKey()); + if (entry.getValue() instanceof JsonPrimitive primitive) { + PlayerData playerData = plugin.getStorageManager().fromJson(primitive.getAsString()); + futures.add(storageProvider.updateOrInsertPlayerData(uuid, playerData, true).thenAccept(it -> userCount.incrementAndGet())); + } + } + + CompletableFuture overallFuture = CompletableFutures.allOf(futures); + + while (true) { + try { + overallFuture.get(3, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + break; + } catch (TimeoutException e) { + handleFeedback(context, MessageConstants.COMMAND_DATA_IMPORT_PROGRESS, Component.text(userCount.get()), Component.text(amount)); + continue; + } + break; + } + + handleFeedback(context, MessageConstants.COMMAND_DATA_IMPORT_SUCCESS); + }); + }); + } + + @Override + public String getFeatureID() { + return "data_import"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ImportItemCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ImportItemCommand.java new file mode 100644 index 00000000..e79610f0 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ImportItemCommand.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import dev.dejvokep.boostedyaml.YamlDocument; +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.bukkit.util.ItemStackUtils; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.parser.standard.StringParser; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +@SuppressWarnings("DuplicatedCode") +public class ImportItemCommand extends BukkitCommandFeature { + + public ImportItemCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .required("id", StringParser.stringParser()) + .flag(manager.flagBuilder("silent").withAliases("s").build()) + .handler(context -> { + Player player = context.sender(); + ItemStack item = player.getInventory().getItemInMainHand(); + String id = context.get("id"); + if (item.getType() == Material.AIR) { + handleFeedback(context, MessageConstants.COMMAND_ITEM_IMPORT_FAILURE_NO_ITEM); + return; + } + File saved = new File(BukkitCustomFishingPlugin.getInstance().getDataFolder(), "imported_items.yml"); + if (!saved.exists()) { + try { + saved.createNewFile(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + YamlDocument document = BukkitCustomFishingPlugin.getInstance().getConfigManager().loadData(saved); + Map map = ItemStackUtils.itemStackToMap(item); + document.set(id, map); + try { + document.save(saved); + handleFeedback(context, MessageConstants.COMMAND_ITEM_IMPORT_SUCCESS, Component.text(id)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + + @Override + public String getFeatureID() { + return "import_item"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/OpenBagCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/OpenBagCommand.java new file mode 100644 index 00000000..19a7b2e5 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/OpenBagCommand.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; + +public class OpenBagCommand extends BukkitCommandFeature { + + public OpenBagCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("player", PlayerParser.playerParser()) + .flag(manager.flagBuilder("silent").withAliases("s").build()) + .handler(context -> { + final Player player = context.get("player"); + BukkitCustomFishingPlugin.getInstance().getBagManager().openBag(player, player.getUniqueId()).whenComplete((result, e) -> { + if (!result || e != null) { + handleFeedback(context, MessageConstants.COMMAND_BAG_OPEN_FAILURE_NOT_LOADED, Component.text(player.getName())); + } else { + handleFeedback(context, MessageConstants.COMMAND_BAG_OPEN_SUCCESS, Component.text(player.getName())); + } + }); + }); + } + + @Override + public String getFeatureID() { + return "open_bag"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/OpenMarketCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/OpenMarketCommand.java new file mode 100644 index 00000000..eef0d5f8 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/OpenMarketCommand.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; + +public class OpenMarketCommand extends BukkitCommandFeature { + + public OpenMarketCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("player", PlayerParser.playerParser()) + .flag(manager.flagBuilder("silent").withAliases("s").build()) + .handler(context -> { + final Player player = context.get("player"); + if (BukkitCustomFishingPlugin.getInstance().getMarketManager().openMarketGUI(player)) { + handleFeedback(context, MessageConstants.COMMAND_MARKET_OPEN_SUCCESS, Component.text(player.getName())); + } else { + handleFeedback(context, MessageConstants.COMMAND_MARKET_OPEN_FAILURE_NOT_LOADED, Component.text(player.getName())); + } + }); + } + + @Override + public String getFeatureID() { + return "open_market"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/QueryStatisticsCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/QueryStatisticsCommand.java new file mode 100644 index 00000000..9aa053fd --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/QueryStatisticsCommand.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.statistic.FishingStatistics; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; +import org.incendo.cloud.parser.standard.EnumParser; + +public class QueryStatisticsCommand extends BukkitCommandFeature { + + public QueryStatisticsCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .flag(manager.flagBuilder("silent").withAliases("s")) + .required("player", PlayerParser.playerParser()) + .required("type", EnumParser.enumParser(FishingStatistics.Type.class)) + .handler(context -> { + Player player = context.get("player"); + FishingStatistics.Type type = context.get("type"); + BukkitCustomFishingPlugin.getInstance().getStorageManager().getOnlineUser(player.getUniqueId()).ifPresentOrElse(userData -> { + if (type == FishingStatistics.Type.AMOUNT_OF_FISH_CAUGHT) { + handleFeedback(context, MessageConstants.COMMAND_STATISTICS_QUERY_AMOUNT, Component.text(userData.statistics().amountMap().toString())); + } else if (type == FishingStatistics.Type.MAX_SIZE) { + handleFeedback(context, MessageConstants.COMMAND_STATISTICS_QUERY_SIZE, Component.text(userData.statistics().sizeMap().toString())); + } + }, () -> handleFeedback(context, MessageConstants.COMMAND_STATISTICS_FAILURE_NOT_LOADED)); + }); + } + + @Override + public String getFeatureID() { + return "statistics_query"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ReloadCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ReloadCommand.java new file mode 100644 index 00000000..67605ebf --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ReloadCommand.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public class ReloadCommand extends BukkitCommandFeature { + + public ReloadCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .flag(manager.flagBuilder("silent").withAliases("s")) + .handler(context -> { + long time1 = System.currentTimeMillis(); + BukkitCustomFishingPlugin.getInstance().reload(); + handleFeedback(context, MessageConstants.COMMAND_RELOAD_SUCCESS, Component.text(System.currentTimeMillis() - time1)); + }); + } + + @Override + public String getFeatureID() { + return "reload"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ResetStatisticsCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ResetStatisticsCommand.java new file mode 100644 index 00000000..eadf2427 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/ResetStatisticsCommand.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; + +public class ResetStatisticsCommand extends BukkitCommandFeature { + + public ResetStatisticsCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .flag(manager.flagBuilder("silent").withAliases("s")) + .required("player", PlayerParser.playerParser()) + .handler(context -> { + Player player = context.get("player"); + BukkitCustomFishingPlugin.getInstance().getStorageManager().getOnlineUser(player.getUniqueId()).ifPresentOrElse(userData -> { + userData.statistics().reset(); + handleFeedback(context, MessageConstants.COMMAND_STATISTICS_RESET_SUCCESS, Component.text(player.getName())); + }, () -> handleFeedback(context, MessageConstants.COMMAND_STATISTICS_FAILURE_NOT_LOADED)); + }); + } + + @Override + public String getFeatureID() { + return "statistics_reset"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/SellFishCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/SellFishCommand.java new file mode 100644 index 00000000..b29759c0 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/SellFishCommand.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public class SellFishCommand extends BukkitCommandFeature { + + public SellFishCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .handler(context -> { + if (!BukkitCustomFishingPlugin.getInstance().getMarketManager().openMarketGUI(context.sender())) { + handleFeedback(context, MessageConstants.COMMAND_DATA_FAILURE_NOT_LOADED); + } + }); + } + + @Override + public String getFeatureID() { + return "sellfish"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/SetStatisticsCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/SetStatisticsCommand.java new file mode 100644 index 00000000..92e16a89 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/SetStatisticsCommand.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.statistic.FishingStatistics; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; +import org.incendo.cloud.parser.standard.DoubleParser; +import org.incendo.cloud.parser.standard.EnumParser; +import org.incendo.cloud.parser.standard.StringParser; + +public class SetStatisticsCommand extends BukkitCommandFeature { + + public SetStatisticsCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .flag(manager.flagBuilder("silent").withAliases("s")) + .required("player", PlayerParser.playerParser()) + .required("id", StringParser.stringParser()) + .required("type", EnumParser.enumParser(FishingStatistics.Type.class)) + .required("value", DoubleParser.doubleParser(0)) + .handler(context -> { + Player player = context.get("player"); + String id = context.get("id"); + FishingStatistics.Type type = context.get("type"); + double value = context.get("value"); + BukkitCustomFishingPlugin.getInstance().getStorageManager().getOnlineUser(player.getUniqueId()).ifPresentOrElse(userData -> { + if (type == FishingStatistics.Type.AMOUNT_OF_FISH_CAUGHT) { + userData.statistics().setAmount(id, (int) value); + handleFeedback(context, MessageConstants.COMMAND_STATISTICS_MODIFY_SUCCESS, Component.text(player.getName())); + } else if (type == FishingStatistics.Type.MAX_SIZE) { + userData.statistics().setMaxSize(id, (float) value); + handleFeedback(context, MessageConstants.COMMAND_STATISTICS_MODIFY_SUCCESS, Component.text(player.getName())); + } + }, () -> handleFeedback(context, MessageConstants.COMMAND_STATISTICS_FAILURE_NOT_LOADED)); + }); + } + + @Override + public String getFeatureID() { + return "statistics_set"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/StartCompetitionCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/StartCompetitionCommand.java new file mode 100644 index 00000000..7d3419c8 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/StartCompetitionCommand.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.concurrent.CompletableFuture; + +public class StartCompetitionCommand extends BukkitCommandFeature { + + public StartCompetitionCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("id", StringParser.stringComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + return CompletableFuture.completedFuture(BukkitCustomFishingPlugin.getInstance().getCompetitionManager().getCompetitionIDs().stream().map(Suggestion::suggestion).toList()); + } + })) + .optional("group", StringParser.stringParser()) + .flag(manager.flagBuilder("silent").withAliases("s").build()) + .handler(context -> { + String id = context.get("id"); + String group = context.getOrDefault("group", null); + if (BukkitCustomFishingPlugin.getInstance().getCompetitionManager().startCompetition(id, true, group)) { + handleFeedback(context, MessageConstants.COMMAND_COMPETITION_START_SUCCESS); + } else { + handleFeedback(context, MessageConstants.COMMAND_COMPETITION_FAILURE_NOT_EXIST, Component.text(id)); + } + }); + } + + @Override + public String getFeatureID() { + return "start_competition"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/StopCompetitionCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/StopCompetitionCommand.java new file mode 100644 index 00000000..02fc38b7 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/StopCompetitionCommand.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public class StopCompetitionCommand extends BukkitCommandFeature { + + public StopCompetitionCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .flag(manager.flagBuilder("silent").withAliases("s").build()) + .handler(context -> { + FishingCompetition competition = BukkitCustomFishingPlugin.getInstance().getCompetitionManager().getOnGoingCompetition(); + if (competition == null) { + handleFeedback(context, MessageConstants.COMMAND_COMPETITION_FAILURE_NO_COMPETITION); + } else { + competition.stop(true); + handleFeedback(context, MessageConstants.COMMAND_COMPETITION_STOP_SUCCESS); + } + }); + } + + @Override + public String getFeatureID() { + return "stop_competition"; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/UnlockDataCommand.java b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/UnlockDataCommand.java new file mode 100644 index 00000000..fee5a3bd --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/command/feature/UnlockDataCommand.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.command.BukkitCommandFeature; +import net.momirealms.customfishing.common.command.CustomFishingCommandManager; +import net.momirealms.customfishing.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.parser.standard.UUIDParser; + +import java.util.UUID; + +public class UnlockDataCommand extends BukkitCommandFeature { + + public UnlockDataCommand(CustomFishingCommandManager commandManager) { + super(commandManager); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .flag(manager.flagBuilder("silent").withAliases("s")) + .required("uuid", UUIDParser.uuidParser()) + .handler(context -> { + UUID uuid = context.get("uuid"); + BukkitCustomFishingPlugin.getInstance().getStorageManager().getDataSource().lockOrUnlockPlayerData(uuid, false); + handleFeedback(context, MessageConstants.COMMAND_DATA_UNLOCK_SUCCESS, Component.text(uuid.toString())); + }); + } + + @Override + public String getFeatureID() { + return "data_unlock"; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/BukkitCompetitionManager.java similarity index 51% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/competition/BukkitCompetitionManager.java index 6493ce22..82012412 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/BukkitCompetitionManager.java @@ -15,24 +15,28 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.competition; +package net.momirealms.customfishing.bukkit.competition; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.manager.CompetitionManager; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.bossbar.BossBar; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; import net.momirealms.customfishing.api.mechanic.action.Action; -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.CFLocale; -import net.momirealms.customfishing.storage.method.database.nosql.RedisManager; -import net.momirealms.customfishing.util.ConfigUtils; +import net.momirealms.customfishing.api.mechanic.action.ActionManager; +import net.momirealms.customfishing.api.mechanic.competition.CompetitionConfig; +import net.momirealms.customfishing.api.mechanic.competition.CompetitionGoal; +import net.momirealms.customfishing.api.mechanic.competition.CompetitionManager; +import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; +import net.momirealms.customfishing.api.mechanic.competition.info.ActionBarConfig; +import net.momirealms.customfishing.api.mechanic.competition.info.BossBarConfig; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.bukkit.storage.method.database.nosql.RedisManager; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customfishing.common.util.Pair; import org.bukkit.Bukkit; -import org.bukkit.boss.BarColor; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.jetbrains.annotations.NotNull; +import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; import java.io.File; @@ -40,16 +44,16 @@ import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.TimeUnit; -public class CompetitionManagerImpl implements CompetitionManager { +public class BukkitCompetitionManager implements CompetitionManager { - private final CustomFishingPlugin plugin; + private final BukkitCustomFishingPlugin plugin; private final HashMap timeConfigMap; private final HashMap commandConfigMap; private Competition currentCompetition; - private CancellableTask timerCheckTask; + private SchedulerTask timerCheckTask; private int nextCompetitionSeconds; - public CompetitionManagerImpl(CustomFishingPlugin plugin) { + public BukkitCompetitionManager(BukkitCustomFishingPlugin plugin) { this.plugin = plugin; this.timeConfigMap = new HashMap<>(); this.commandConfigMap = new HashMap<>(); @@ -57,42 +61,31 @@ public class CompetitionManagerImpl implements CompetitionManager { public void load() { loadConfig(); - this.timerCheckTask = plugin.getScheduler().runTaskAsyncTimer( + this.timerCheckTask = plugin.getScheduler().asyncRepeating( this::timerCheck, 1, 1, TimeUnit.SECONDS ); + plugin.debug("Loaded " + commandConfigMap.size() + " competitions"); } public void unload() { - if (this.timerCheckTask != null && !this.timerCheckTask.isCancelled()) + if (this.timerCheckTask != null) this.timerCheckTask.cancel(); + if (currentCompetition != null && currentCompetition.isOnGoing()) + this.currentCompetition.stop(true); this.commandConfigMap.clear(); this.timeConfigMap.clear(); - if (currentCompetition != null && currentCompetition.isOnGoing()) { - plugin.getScheduler().runTaskAsync(() -> currentCompetition.stop(true)); - } } public void disable() { - if (this.timerCheckTask != null && !this.timerCheckTask.isCancelled()) + if (this.timerCheckTask != null) this.timerCheckTask.cancel(); + if (currentCompetition != null && currentCompetition.isOnGoing()) + this.currentCompetition.stop(false); this.commandConfigMap.clear(); this.timeConfigMap.clear(); - if (currentCompetition != null && currentCompetition.isOnGoing()) - currentCompetition.stop(false); - } - - /** - * Retrieves a set of all competition names. - * - * @return A set of competition names. - */ - @NotNull - @Override - public Set getAllCompetitionKeys() { - return commandConfigMap.keySet(); } @SuppressWarnings("DuplicatedCode") @@ -102,7 +95,7 @@ public class CompetitionManagerImpl implements CompetitionManager { File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type); if (!typeFolder.exists()) { if (!typeFolder.mkdirs()) return; - plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false); + plugin.getBoostrap().saveResource("contents" + File.separator + type + File.separator + "default.yml", false); } fileDeque.push(typeFolder); while (!fileDeque.isEmpty()) { @@ -121,46 +114,52 @@ public class CompetitionManagerImpl implements CompetitionManager { } private void loadSingleFileCompetition(File file) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - for (Map.Entry entry : config.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection section) { - - CompetitionConfig.Builder builder = new CompetitionConfig.Builder(entry.getKey()) - .goal(CompetitionGoal.valueOf(section.getString("goal", "TOTAL_SCORE").toUpperCase(Locale.ENGLISH))) + YamlDocument document = plugin.getConfigManager().loadData(file); + for (Map.Entry entry : document.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section section) { + CompetitionConfig.Builder builder = CompetitionConfig.builder() + .key(entry.getKey()) + .goal(CompetitionGoal.index().value(section.getString("goal", "TOTAL_SCORE").toLowerCase(Locale.ENGLISH))) .minPlayers(section.getInt("min-players", 0)) .duration(section.getInt("duration", 300)) - .rewards(getPrizeActions(section.getConfigurationSection("rewards"))) - .requirements(plugin.getRequirementManager().getRequirements(section.getConfigurationSection("participate-requirements"), false)) - .joinActions(plugin.getActionManager().getActions(section.getConfigurationSection("participate-actions"))) - .startActions(plugin.getActionManager().getActions(section.getConfigurationSection("start-actions"))) - .endActions(plugin.getActionManager().getActions(section.getConfigurationSection("end-actions"))) - .skipActions(plugin.getActionManager().getActions(section.getConfigurationSection("skip-actions"))); - + .rewards(getPrizeActions(section.getSection("rewards"))) + .joinRequirements(plugin.getRequirementManager().parseRequirements(section.getSection("participate-requirements"), false)) + .joinActions(plugin.getActionManager().parseActions(section.getSection("participate-actions"))) + .startActions(plugin.getActionManager().parseActions(section.getSection("start-actions"))) + .endActions(plugin.getActionManager().parseActions(section.getSection("end-actions"))) + .skipActions(plugin.getActionManager().parseActions(section.getSection("skip-actions")));; if (section.getBoolean("bossbar.enable", false)) { - builder.bossbar(new BossBarConfig.Builder() - .color(BarColor.valueOf(section.getString("bossbar.color", "WHITE").toUpperCase(Locale.ENGLISH))) - .overlay(BossBarConfig.Overlay.valueOf(section.getString("bossbar.overlay", "PROGRESS").toUpperCase(Locale.ENGLISH))) - .refreshRate(section.getInt("bossbar.refresh-rate", 20)) - .switchInterval(section.getInt("bossbar.switch-interval", 200)) - .showToAll(!section.getBoolean("bossbar.only-show-to-participants", true)) - .text(section.getStringList("bossbar.text").toArray(new String[0])) - .build()); + builder.bossBarConfig( + BossBarConfig.builder() + .enable(true) + .color(BossBar.Color.valueOf(section.getString("bossbar.color", "WHITE").toUpperCase(Locale.ENGLISH))) + .overlay(BossBar.Overlay.valueOf(section.getString("bossbar.overlay", "PROGRESS").toUpperCase(Locale.ENGLISH))) + .refreshRate(section.getInt("bossbar.refresh-rate", 20)) + .switchInterval(section.getInt("bossbar.switch-interval", 200)) + .showToAll(!section.getBoolean("bossbar.only-show-to-participants", true)) + .text(section.getStringList("bossbar.text").toArray(new String[0])) + .build() + ); } - if (section.getBoolean("actionbar.enable", false)) { - builder.actionbar(new ActionBarConfig.Builder() - .refreshRate(section.getInt("actionbar.refresh-rate", 5)) - .switchInterval(section.getInt("actionbar.switch-interval", 200)) - .showToAll(!section.getBoolean("actionbar.only-show-to-participants", true)) - .text(section.getStringList("actionbar.text").toArray(new String[0])) - .build()); + builder.actionBarConfig( + ActionBarConfig.builder() + .enable(true) + .refreshRate(section.getInt("actionbar.refresh-rate", 5)) + .switchInterval(section.getInt("actionbar.switch-interval", 200)) + .showToAll(!section.getBoolean("actionbar.only-show-to-participants", true)) + .text(section.getStringList("actionbar.text").toArray(new String[0])) + .build() + ); } - CompetitionConfig competitionConfig = builder.build(); List> timePairs = section.getStringList("start-time") - .stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, ":")).toList(); - List weekdays = section.getIntegerList("start-weekday"); - if (weekdays.size() == 0) { + .stream().map(it -> { + String[] split = it.split(":"); + return Pair.of(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + }).toList(); + List weekdays = section.getIntList("start-weekday"); + if (weekdays.isEmpty()) { weekdays.addAll(List.of(1,2,3,4,5,6,7)); } for (Integer weekday : weekdays) { @@ -180,12 +179,12 @@ public class CompetitionManagerImpl implements CompetitionManager { * @param section The configuration section containing prize actions. * @return A HashMap where keys are action names and values are arrays of Action objects. */ - public HashMap getPrizeActions(ConfigurationSection section) { - HashMap map = new HashMap<>(); + public HashMap[]> getPrizeActions(Section section) { + HashMap[]> map = new HashMap<>(); if (section == null) return map; - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - map.put(entry.getKey(), plugin.getActionManager().getActions(innerSection)); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + map.put(entry.getKey(), plugin.getActionManager().parseActions(innerSection)); } } return map; @@ -214,37 +213,10 @@ 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 getCompetitionGoalLocale(CompetitionGoal goal) { - switch (goal) { - case MAX_SIZE -> { - return CFLocale.MSG_Max_Size; - } - case CATCH_AMOUNT -> { - return CFLocale.MSG_Catch_Amount; - } - case TOTAL_SCORE -> { - return CFLocale.MSG_Total_Score; - } - case TOTAL_SIZE -> { - return CFLocale.MSG_Total_Size; - } - } - return ""; - } - @Override public boolean startCompetition(String competition, boolean force, String serverGroup) { CompetitionConfig config = commandConfigMap.get(competition); if (config == null) { - LogUtils.warn("Competition " + competition + " doesn't exist."); return false; } return startCompetition(config, force, serverGroup); @@ -266,14 +238,8 @@ public class CompetitionManagerImpl implements CompetitionManager { public boolean startCompetition(CompetitionConfig config, boolean force, @Nullable String serverGroup) { if (!force) { int players = Bukkit.getOnlinePlayers().size(); - if (players < config.getMinPlayersToStart()) { - var actions = config.getSkipActions(); - if (actions != null) { - Condition condition = new Condition(null, null, new HashMap<>()); - for (Action action : actions) { - action.trigger(condition); - } - } + if (players < config.minPlayersToStart()) { + ActionManager.trigger(Context.player(null), config.skipActions()); return false; } start(config); @@ -282,7 +248,12 @@ public class CompetitionManagerImpl implements CompetitionManager { start(config); return true; } else { - RedisManager.getInstance().publishRedisMessage(serverGroup, "start;" + config.getKey()); + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF(serverGroup); + out.writeUTF("competition"); + out.writeUTF("start"); + out.writeUTF(config.key()); + RedisManager.getInstance().publishRedisMessage(Arrays.toString(out.toByteArray())); return true; } } @@ -291,16 +262,16 @@ public class CompetitionManagerImpl implements CompetitionManager { if (getOnGoingCompetition() != null) { // END currentCompetition.end(true); - plugin.getScheduler().runTaskAsyncLater(() -> { + plugin.getScheduler().asyncLater(() -> { // start one second later - this.currentCompetition = new Competition(config); - this.currentCompetition.start(); + this.currentCompetition = new Competition(plugin, config); + this.currentCompetition.start(true); }, 1, TimeUnit.SECONDS); } else { // start instantly - plugin.getScheduler().runTaskAsync(() -> { - this.currentCompetition = new Competition(config); - this.currentCompetition.start(); + plugin.getScheduler().async().execute(() -> { + this.currentCompetition = new Competition(plugin, config); + this.currentCompetition.start(true); }); } } @@ -311,19 +282,18 @@ public class CompetitionManagerImpl implements CompetitionManager { * @return The number of seconds until the next competition. */ @Override - public int getNextCompetitionSeconds() { + public int getNextCompetitionInSeconds() { return nextCompetitionSeconds; } - /** - * 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) { + public CompetitionConfig getCompetition(String key) { return commandConfigMap.get(key); } + + @Override + public Collection getCompetitionIDs() { + return commandConfigMap.keySet(); + } } diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/competition/Competition.java b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/Competition.java new file mode 100644 index 00000000..b3daf267 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/Competition.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.competition; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.event.CompetitionEvent; +import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.action.ActionManager; +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 net.momirealms.customfishing.api.mechanic.competition.RankingProvider; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.bukkit.competition.actionbar.ActionBarManager; +import net.momirealms.customfishing.bukkit.competition.bossbar.BossBarManager; +import net.momirealms.customfishing.bukkit.competition.ranking.LocalRankingProvider; +import net.momirealms.customfishing.bukkit.competition.ranking.RedisRankingProvider; +import net.momirealms.customfishing.common.locale.MessageConstants; +import net.momirealms.customfishing.common.locale.TranslationManager; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customfishing.common.util.Pair; +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.Optional; +import java.util.concurrent.TimeUnit; + +public class Competition implements FishingCompetition { + + private final BukkitCustomFishingPlugin plugin; + private final CompetitionConfig config; + private SchedulerTask competitionTimerTask; + private final CompetitionGoal goal; + private final Context publicContext; + private final RankingProvider rankingProvider; + private float progress; + private int remainingTime; + private long startTime; + private BossBarManager bossBarManager; + private ActionBarManager actionBarManager; + + public Competition(BukkitCustomFishingPlugin plugin, CompetitionConfig config) { + this.config = config; + this.plugin = plugin; + this.goal = config.goal() == CompetitionGoal.RANDOM ? CompetitionGoal.getRandom() : config.goal(); + if (ConfigManager.redisRanking()) this.rankingProvider = new RedisRankingProvider(); + else this.rankingProvider = new LocalRankingProvider(); + this.publicContext = Context.player(null, true); + this.publicContext.arg(ContextKeys.GOAL, goal); + } + + @Override + public void start(boolean triggerEvent) { + this.progress = 1; + this.remainingTime = this.config.durationInSeconds(); + this.startTime = Instant.now().getEpochSecond(); + + this.arrangeTimerTask(); + if (this.config.bossBarConfig() != null && this.config.bossBarConfig().enabled()) { + this.bossBarManager = new BossBarManager(this.config.bossBarConfig(), this); + this.bossBarManager.load(); + } + if (this.config.actionBarConfig() != null && this.config.actionBarConfig().enabled()) { + this.actionBarManager = new ActionBarManager(this.config.actionBarConfig(), this); + this.actionBarManager.load(); + } + + this.updatePublicPlaceholders(); + ActionManager.trigger(this.publicContext, this.config.startActions()); + this.rankingProvider.clear(); + if (triggerEvent) { + this.plugin.getScheduler().async().execute(() -> { + CompetitionEvent competitionStartEvent = new CompetitionEvent(CompetitionEvent.State.START, this); + Bukkit.getPluginManager().callEvent(competitionStartEvent); + }); + } + } + + @Override + public void stop(boolean triggerEvent) { + if (this.competitionTimerTask != null) + this.competitionTimerTask.cancel(); + if (this.bossBarManager != null) + this.bossBarManager.unload(); + if (this.actionBarManager != null) + this.actionBarManager.unload(); + this.rankingProvider.clear(); + this.remainingTime = 0; + if (triggerEvent) { + plugin.getScheduler().async().execute(() -> { + CompetitionEvent competitionEvent = new CompetitionEvent(CompetitionEvent.State.STOP, this); + Bukkit.getPluginManager().callEvent(competitionEvent); + }); + } + } + + @Override + public void end(boolean triggerEvent) { + // mark it as ended + this.remainingTime = 0; + + // cancel some sub tasks + if (competitionTimerTask != null) + this.competitionTimerTask.cancel(); + if (this.bossBarManager != null) + this.bossBarManager.unload(); + if (this.actionBarManager != null) + this.actionBarManager.unload(); + + // give prizes + HashMap[]> rewardsMap = config.rewards(); + if (rankingProvider.getSize() != 0 && rewardsMap != null) { + Iterator> iterator = rankingProvider.getIterator(); + int i = 1; + while (iterator.hasNext()) { + Pair competitionPlayer = iterator.next(); + this.publicContext.arg(ContextKeys.of(i + "_player", String.class), competitionPlayer.left()); + this.publicContext.arg(ContextKeys.of(i + "_score", String.class), String.format("%.2f", competitionPlayer.right())); + if (i < rewardsMap.size()) { + Player player = Bukkit.getPlayer(competitionPlayer.left()); + if (player != null) { + ActionManager.trigger(Context.player(player).combine(this.publicContext), rewardsMap.get(String.valueOf(i))); + } + } else { + Action[] actions = rewardsMap.get("participation"); + if (actions != null) { + Player player = Bukkit.getPlayer(competitionPlayer.left()); { + if (player != null) { + ActionManager.trigger(Context.player(player).combine(this.publicContext), actions); + } + } + } + } + i++; + } + } + + // end actions + ActionManager.trigger(publicContext, config.endActions()); + + // call event + if (triggerEvent) { + plugin.getScheduler().async().execute(() -> { + CompetitionEvent competitionEndEvent = new CompetitionEvent(CompetitionEvent.State.END, this); + Bukkit.getPluginManager().callEvent(competitionEndEvent); + }); + } + + // 1 seconds delay for other servers to read the redis data + plugin.getScheduler().asyncLater(this.rankingProvider::clear, 1, TimeUnit.SECONDS); + } + + private void arrangeTimerTask() { + this.competitionTimerTask = this.plugin.getScheduler().asyncRepeating(() -> { + if (decreaseTime()) { + end(true); + return; + } + updatePublicPlaceholders(); + }, 1, 1, TimeUnit.SECONDS); + } + + private void updatePublicPlaceholders() { + for (int i = 1; i < ConfigManager.placeholderLimit() + 1; i++) { + Optional player = Optional.ofNullable(this.rankingProvider.getPlayerAt(i)); + if (player.isPresent()) { + this.publicContext.arg(ContextKeys.of(i + "_player", String.class), player.get()); + this.publicContext.arg(ContextKeys.of(i + "_score", String.class), String.format("%.2f", this.rankingProvider.getScoreAt(i))); + } else { + this.publicContext.arg(ContextKeys.of(i + "_player", String.class), TranslationManager.miniMessageTranslation(MessageConstants.COMPETITION_NO_PLAYER.build().key())); + this.publicContext.arg(ContextKeys.of(i + "_score", String.class), TranslationManager.miniMessageTranslation(MessageConstants.COMPETITION_NO_SCORE.build().key())); + } + } + this.publicContext.arg(ContextKeys.HOUR, remainingTime < 3600 ? "" : (remainingTime / 3600) + TranslationManager.miniMessageTranslation(MessageConstants.FORMAT_HOUR.build().key())); + this.publicContext.arg(ContextKeys.MINUTE, remainingTime < 60 ? "" : (remainingTime % 3600) / 60 + TranslationManager.miniMessageTranslation(MessageConstants.FORMAT_MINUTE.build().key())); + this.publicContext.arg(ContextKeys.SECOND, remainingTime == 0 ? "" : remainingTime % 60 + TranslationManager.miniMessageTranslation(MessageConstants.FORMAT_SECOND.build().key())); + this.publicContext.arg(ContextKeys.SECONDS, remainingTime); + } + + @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.durationInSeconds(); + remainingTime = (int) (duration - (current - startTime)); + progress = (float) remainingTime / duration; + return remainingTime <= 0; + } + + @Override + public void refreshData(Player player, double score) { + // if player join for the first time, trigger join actions + if (!hasPlayerJoined(player)) { + ActionManager.trigger(Context.player(player).combine(publicContext), config.joinActions()); + } + + // show competition info + if (this.bossBarManager != null) + this.bossBarManager.showBossBarTo(player); + if (this.actionBarManager != null) + this.actionBarManager.showActionBarTo(player); + + // refresh data + this.goal.refreshScore(rankingProvider, player, score); + } + + @Override + public boolean hasPlayerJoined(OfflinePlayer player) { + return rankingProvider.getPlayerRank(player.getName()) != -1; + } + + @Override + public float getProgress() { + return progress; + } + + @Override + public long getRemainingTime() { + return remainingTime; + } + + @Override + public long getStartTime() { + return startTime; + } + + @NotNull + @Override + public CompetitionConfig getConfig() { + return config; + } + + @NotNull + @Override + public CompetitionGoal getGoal() { + return goal; + } + + @NotNull + @Override + public RankingProvider getRanking() { + return rankingProvider; + } + + @Override + public Context getPublicContext() { + return publicContext; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionSchedule.java b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/CompetitionSchedule.java similarity index 98% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionSchedule.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/competition/CompetitionSchedule.java index 03564cf3..63eb82b9 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionSchedule.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/CompetitionSchedule.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.competition; +package net.momirealms.customfishing.bukkit.competition; public class CompetitionSchedule { diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/actionbar/ActionBarManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/actionbar/ActionBarManager.java similarity index 86% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/actionbar/ActionBarManager.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/competition/actionbar/ActionBarManager.java index 1bb6b546..71cc49be 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/actionbar/ActionBarManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/actionbar/ActionBarManager.java @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.competition.actionbar; +package net.momirealms.customfishing.bukkit.competition.actionbar; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.mechanic.competition.ActionBarConfig; -import net.momirealms.customfishing.mechanic.competition.Competition; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.competition.info.ActionBarConfig; +import net.momirealms.customfishing.bukkit.competition.Competition; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -47,8 +47,8 @@ public class ActionBarManager implements Listener { * Loads the ActionBar manager, registering events and showing ActionBar messages to online players. */ public void load() { - Bukkit.getPluginManager().registerEvents(this, CustomFishingPlugin.getInstance()); - if (actionBarConfig.isShowToAll()) { + Bukkit.getPluginManager().registerEvents(this, BukkitCustomFishingPlugin.getInstance().getBoostrap()); + if (actionBarConfig.showToAll()) { for (Player player : Bukkit.getOnlinePlayers()) { ActionBarSender sender = new ActionBarSender(player, actionBarConfig, competition); if (!sender.isVisible()) { @@ -93,9 +93,9 @@ public class ActionBarManager implements Listener { @EventHandler public void onJoin(PlayerJoinEvent event) { final Player player = event.getPlayer(); - CustomFishingPlugin.getInstance().getScheduler().runTaskAsyncLater(() -> { + BukkitCustomFishingPlugin.getInstance().getScheduler().asyncLater(() -> { boolean hasJoined = competition.hasPlayerJoined(player); - if ((hasJoined || actionBarConfig.isShowToAll()) + if ((hasJoined || actionBarConfig.showToAll()) && !senderMap.containsKey(player.getUniqueId())) { ActionBarSender sender = new ActionBarSender(player, actionBarConfig, competition); if (!sender.isVisible()) { @@ -117,8 +117,7 @@ public class ActionBarManager implements Listener { sender = new ActionBarSender(player, actionBarConfig, competition); senderMap.put(player.getUniqueId(), sender); } - if (!sender.isVisible()) { + if (!sender.isVisible()) sender.show(); - } } } diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/competition/actionbar/ActionBarSender.java b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/actionbar/ActionBarSender.java new file mode 100644 index 00000000..1dc3e3fa --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/actionbar/ActionBarSender.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.competition.actionbar; + +import net.kyori.adventure.audience.Audience; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.competition.info.ActionBarConfig; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.misc.value.DynamicText; +import net.momirealms.customfishing.bukkit.competition.Competition; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import net.momirealms.customfishing.common.locale.MessageConstants; +import net.momirealms.customfishing.common.locale.TranslationManager; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import org.bukkit.entity.Player; + +import java.util.concurrent.TimeUnit; + +public class ActionBarSender { + + private final Player player; + private final Audience audience; + private int refreshTimer; + private int switchTimer; + private int counter; + private final DynamicText[] texts; + private SchedulerTask senderTask; + private final ActionBarConfig config; + private boolean isShown; + private final Competition competition; + private final Context privateContext; + + public ActionBarSender(Player player, ActionBarConfig config, Competition competition) { + this.player = player; + this.audience = BukkitCustomFishingPlugin.getInstance().getSenderFactory().getAudience(player); + this.config = config; + this.privateContext = Context.player(player); + this.isShown = false; + this.competition = competition; + this.updatePrivatePlaceholders(); + String[] str = config.texts(); + texts = new DynamicText[str.length]; + for (int i = 0; i < str.length; i++) { + texts[i] = new DynamicText(player, str[i]); + texts[i].update(privateContext.placeholderMap()); + } + } + + @SuppressWarnings("DuplicatedCode") + private void updatePrivatePlaceholders() { + this.privateContext.arg(ContextKeys.SCORE_FORMATTED, String.format("%.2f", competition.getRanking().getPlayerScore(player.getName()))); + int rank = competition.getRanking().getPlayerRank(player.getName()); + this.privateContext.arg(ContextKeys.RANK, rank != -1 ? String.valueOf(rank) : TranslationManager.miniMessageTranslation(MessageConstants.COMPETITION_NO_RANK.build().key())); + this.privateContext.combine(competition.getPublicContext()); + } + + public void show() { + this.isShown = true; + senderTask = BukkitCustomFishingPlugin.getInstance().getScheduler().asyncRepeating(() -> { + switchTimer++; + if (switchTimer > config.switchInterval()) { + switchTimer = 0; + counter++; + } + if (refreshTimer < config.refreshRate()){ + refreshTimer++; + } else { + refreshTimer = 0; + DynamicText text = texts[counter % (texts.length)]; + updatePrivatePlaceholders(); + text.update(this.privateContext.placeholderMap()); + audience.sendActionBar(AdventureHelper.miniMessage(text.getLatestValue())); + } + }, 50, 50, TimeUnit.MILLISECONDS); + } + + public void hide() { + if (senderTask != null) + senderTask.cancel(); + this.isShown = false; + } + + public boolean isVisible() { + return this.isShown; + } + + public ActionBarConfig getConfig() { + return config; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/bossbar/BossBarManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/bossbar/BossBarManager.java similarity index 86% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/bossbar/BossBarManager.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/competition/bossbar/BossBarManager.java index 1dccd3d5..7d26fde5 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/bossbar/BossBarManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/bossbar/BossBarManager.java @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.competition.bossbar; +package net.momirealms.customfishing.bukkit.competition.bossbar; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.mechanic.competition.BossBarConfig; -import net.momirealms.customfishing.mechanic.competition.Competition; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.competition.info.BossBarConfig; +import net.momirealms.customfishing.bukkit.competition.Competition; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -47,8 +47,8 @@ public class BossBarManager implements Listener { * Loads the boss bar manager, registering events and showing boss bars to online players. */ public void load() { - Bukkit.getPluginManager().registerEvents(this, CustomFishingPlugin.getInstance()); - if (bossBarConfig.isShowToAll()) { + Bukkit.getPluginManager().registerEvents(this, BukkitCustomFishingPlugin.getInstance().getBoostrap()); + if (bossBarConfig.showToAll()) { for (Player player : Bukkit.getOnlinePlayers()) { BossBarSender sender = new BossBarSender(player, bossBarConfig, competition); if (!sender.isVisible()) { @@ -93,9 +93,9 @@ public class BossBarManager implements Listener { @EventHandler public void onJoin(PlayerJoinEvent event) { final Player player = event.getPlayer(); - CustomFishingPlugin.getInstance().getScheduler().runTaskAsyncLater(() -> { + BukkitCustomFishingPlugin.getInstance().getScheduler().asyncLater(() -> { boolean hasJoined = competition.hasPlayerJoined(player); - if ((hasJoined || bossBarConfig.isShowToAll()) + if ((hasJoined || bossBarConfig.showToAll()) && !senderMap.containsKey(player.getUniqueId())) { BossBarSender sender = new BossBarSender(player, bossBarConfig, competition); if (!sender.isVisible()) { @@ -117,8 +117,7 @@ public class BossBarManager implements Listener { sender = new BossBarSender(player, bossBarConfig, competition); senderMap.put(player.getUniqueId(), sender); } - if (!sender.isVisible()) { + if (!sender.isVisible()) sender.show(); - } } } diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/competition/bossbar/BossBarSender.java b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/bossbar/BossBarSender.java new file mode 100644 index 00000000..c6489877 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/bossbar/BossBarSender.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.competition.bossbar; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.bossbar.BossBar; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.competition.info.BossBarConfig; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.misc.value.DynamicText; +import net.momirealms.customfishing.bukkit.competition.Competition; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import net.momirealms.customfishing.common.locale.MessageConstants; +import net.momirealms.customfishing.common.locale.TranslationManager; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import org.bukkit.entity.Player; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +public class BossBarSender { + + private final Player player; + private final Audience audience; + private int refreshTimer; + private int switchTimer; + private int counter; + private final DynamicText[] texts; + private SchedulerTask senderTask; + private final BossBar bossBar; + private final BossBarConfig config; + private boolean isShown; + private final Competition competition; + private final Context privateContext; + + public BossBarSender(Player player, BossBarConfig config, Competition competition) { + this.player = player; + this.audience = BukkitCustomFishingPlugin.getInstance().getSenderFactory().getAudience(player); + this.config = config; + this.isShown = false; + this.competition = competition; + this.privateContext = Context.player(player); + this.updatePrivatePlaceholders(); + String[] str = config.texts(); + texts = new DynamicText[str.length]; + for (int i = 0; i < str.length; i++) { + texts[i] = new DynamicText(player, str[i]); + texts[i].update(privateContext.placeholderMap()); + } + bossBar = BossBar.bossBar( + AdventureHelper.miniMessage(texts[0].getLatestValue()), + competition.getProgress(), + config.color(), + config.overlay(), + Set.of() + ); + } + + @SuppressWarnings("DuplicatedCode") + private void updatePrivatePlaceholders() { + this.privateContext.arg(ContextKeys.SCORE_FORMATTED, String.format("%.2f", competition.getRanking().getPlayerScore(player.getName()))); + int rank = competition.getRanking().getPlayerRank(player.getName()); + this.privateContext.arg(ContextKeys.RANK, rank != -1 ? String.valueOf(rank) : TranslationManager.miniMessageTranslation(MessageConstants.COMPETITION_NO_RANK.build().key())); + this.privateContext.combine(competition.getPublicContext()); + } + + public void show() { + this.isShown = true; + this.bossBar.addViewer(audience); + this.senderTask = BukkitCustomFishingPlugin.getInstance().getScheduler().asyncRepeating(() -> { + switchTimer++; + if (switchTimer > config.switchInterval()) { + switchTimer = 0; + counter++; + } + if (refreshTimer < config.refreshRate()){ + refreshTimer++; + } else { + refreshTimer = 0; + DynamicText text = texts[counter % (texts.length)]; + updatePrivatePlaceholders(); + if (text.update(privateContext.placeholderMap())) { + bossBar.name(AdventureHelper.miniMessage(text.getLatestValue())); + } + bossBar.progress(competition.getProgress()); + } + }, 50, 50, TimeUnit.MILLISECONDS); + } + + public boolean isVisible() { + return this.isShown; + } + + public BossBarConfig getConfig() { + return config; + } + + public void hide() { + this.bossBar.removeViewer(audience); + if (senderTask != null) senderTask.cancel(); + this.isShown = false; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/LocalRankingImpl.java b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/ranking/LocalRankingProvider.java similarity index 95% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/LocalRankingImpl.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/competition/ranking/LocalRankingProvider.java index 8b00564d..64b77f06 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/LocalRankingImpl.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/ranking/LocalRankingProvider.java @@ -15,22 +15,22 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.competition.ranking; +package net.momirealms.customfishing.bukkit.competition.ranking; -import net.momirealms.customfishing.api.common.Pair; import net.momirealms.customfishing.api.mechanic.competition.CompetitionPlayer; -import net.momirealms.customfishing.api.mechanic.competition.Ranking; +import net.momirealms.customfishing.api.mechanic.competition.RankingProvider; +import net.momirealms.customfishing.common.util.Pair; import java.util.*; /** * Implementation of the Ranking interface that manages the ranking of competition players locally. */ -public class LocalRankingImpl implements Ranking { +public class LocalRankingProvider implements RankingProvider { private final Set competitionPlayers; - public LocalRankingImpl() { + public LocalRankingProvider() { competitionPlayers = Collections.synchronizedSet(new TreeSet<>()); } @@ -102,9 +102,9 @@ public class LocalRankingImpl implements Ranking { } /** - * Returns an iterator for iterating over pairs of player names and scores. + * Returns an iterator for iterating over items of player names and scores. * - * @return An iterator for pairs of player names and scores. + * @return An iterator for items of player names and scores. */ @Override public Iterator> getIterator() { diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/RedisRankingImpl.java b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/ranking/RedisRankingProvider.java similarity index 76% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/RedisRankingImpl.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/competition/ranking/RedisRankingProvider.java index 9c2a96f7..46739669 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/ranking/RedisRankingImpl.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/competition/ranking/RedisRankingProvider.java @@ -15,20 +15,20 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.competition.ranking; +package net.momirealms.customfishing.bukkit.competition.ranking; -import net.momirealms.customfishing.api.common.Pair; import net.momirealms.customfishing.api.mechanic.competition.CompetitionPlayer; -import net.momirealms.customfishing.api.mechanic.competition.Ranking; -import net.momirealms.customfishing.setting.CFConfig; -import net.momirealms.customfishing.storage.method.database.nosql.RedisManager; +import net.momirealms.customfishing.api.mechanic.competition.RankingProvider; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.bukkit.storage.method.database.nosql.RedisManager; +import net.momirealms.customfishing.common.util.Pair; import redis.clients.jedis.Jedis; import redis.clients.jedis.resps.Tuple; import java.util.Iterator; import java.util.List; -public class RedisRankingImpl implements Ranking { +public class RedisRankingProvider implements RankingProvider { /** * Clears the ranking data by removing all players and scores. @@ -36,7 +36,7 @@ public class RedisRankingImpl implements Ranking { @Override public void clear() { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - jedis.del("cf_competition_" + CFConfig.serverGroup); + jedis.del("cf_competition_" + ConfigManager.serverGroup()); } } @@ -49,7 +49,7 @@ public class RedisRankingImpl implements Ranking { @Override public CompetitionPlayer getCompetitionPlayer(String player) { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - Double score = jedis.zscore("cf_competition_" + CFConfig.serverGroup, player); + Double score = jedis.zscore("cf_competition_" + ConfigManager.serverGroup(), player); if (score == null || score == 0) return null; return new CompetitionPlayer(player, Float.parseFloat(score.toString())); } @@ -58,8 +58,8 @@ public class RedisRankingImpl implements Ranking { @Override public CompetitionPlayer getCompetitionPlayer(int rank) { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - List player = jedis.zrevrangeWithScores("cf_competition_" + CFConfig.serverGroup, rank - 1, rank -1); - if (player == null || player.size() == 0) return null; + List player = jedis.zrevrangeWithScores("cf_competition_" + ConfigManager.serverGroup(), rank - 1, rank -1); + if (player == null || player.isEmpty()) return null; return new CompetitionPlayer(player.get(0).getElement(), player.get(0).getScore()); } } @@ -67,26 +67,26 @@ public class RedisRankingImpl implements Ranking { @Override public void addPlayer(CompetitionPlayer competitionPlayer) { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - jedis.zincrby("cf_competition_" + CFConfig.serverGroup, competitionPlayer.getScore(), competitionPlayer.getPlayer()); + jedis.zincrby("cf_competition_" + ConfigManager.serverGroup(), competitionPlayer.getScore(), competitionPlayer.getPlayer()); } } @Override public void removePlayer(String player) { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - jedis.zrem("cf_competition_" + CFConfig.serverGroup, player); + jedis.zrem("cf_competition_" + ConfigManager.serverGroup(), player); } } /** - * Returns an iterator for iterating over pairs of player names and scores in descending order. + * Returns an iterator for iterating over items of player names and scores in descending order. * - * @return An iterator for pairs of player names and scores. + * @return An iterator for items of player names and scores. */ @Override public Iterator> getIterator() { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - List players = jedis.zrevrangeWithScores("cf_competition_" + CFConfig.serverGroup, 0, -1); + List players = jedis.zrevrangeWithScores("cf_competition_" + ConfigManager.serverGroup(), 0, -1); return players.stream().map(it -> Pair.of(it.getElement(), it.getScore())).toList().iterator(); } } @@ -99,7 +99,7 @@ public class RedisRankingImpl implements Ranking { @Override public int getSize() { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - long size = jedis.zcard("cf_competition_" + CFConfig.serverGroup); + long size = jedis.zcard("cf_competition_" + ConfigManager.serverGroup()); return (int) size; } } @@ -113,7 +113,7 @@ public class RedisRankingImpl implements Ranking { @Override public int getPlayerRank(String player) { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - Long rank = jedis.zrevrank("cf_competition_" + CFConfig.serverGroup, player); + Long rank = jedis.zrevrank("cf_competition_" + ConfigManager.serverGroup(), player); if (rank == null) return -1; return (int) (rank + 1); @@ -129,7 +129,7 @@ public class RedisRankingImpl implements Ranking { @Override public double getPlayerScore(String player) { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - Double rank = jedis.zscore("cf_competition_" + CFConfig.serverGroup, player); + Double rank = jedis.zscore("cf_competition_" + ConfigManager.serverGroup(), player); if (rank == null) return 0; return rank.floatValue(); @@ -145,7 +145,7 @@ public class RedisRankingImpl implements Ranking { @Override public void refreshData(String player, double score) { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - jedis.zincrby("cf_competition_" + CFConfig.serverGroup, score, player); + jedis.zincrby("cf_competition_" + ConfigManager.serverGroup(), score, player); } } @@ -158,7 +158,7 @@ public class RedisRankingImpl implements Ranking { @Override public void setData(String player, double score) { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - jedis.zadd("cf_competition_" + CFConfig.serverGroup, score, player); + jedis.zadd("cf_competition_" + ConfigManager.serverGroup(), score, player); } } @@ -171,8 +171,8 @@ public class RedisRankingImpl implements Ranking { @Override public String getPlayerAt(int rank) { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - List player = jedis.zrevrange("cf_competition_" + CFConfig.serverGroup, rank - 1, rank -1); - if (player == null || player.size() == 0) return null; + List player = jedis.zrevrange("cf_competition_" + ConfigManager.serverGroup(), rank - 1, rank -1); + if (player == null || player.isEmpty()) return null; return player.get(0); } } @@ -186,8 +186,8 @@ public class RedisRankingImpl implements Ranking { @Override public double getScoreAt(int rank) { try (Jedis jedis = RedisManager.getInstance().getJedis()) { - List players = jedis.zrevrangeWithScores("cf_competition_" + CFConfig.serverGroup, rank - 1, rank -1); - if (players == null || players.size() == 0) return 0; + List players = jedis.zrevrangeWithScores("cf_competition_" + ConfigManager.serverGroup(), rank - 1, rank -1); + if (players == null || players.isEmpty()) return 0; return players.get(0).getScore(); } } diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java new file mode 100644 index 00000000..af1c2863 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java @@ -0,0 +1,1081 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.config; + +import com.saicone.rtag.RtagItem; +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; +import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; +import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; +import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; +import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.config.ConfigType; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.effect.EffectProperties; +import net.momirealms.customfishing.api.mechanic.event.EventManager; +import net.momirealms.customfishing.api.mechanic.item.ItemEditor; +import net.momirealms.customfishing.api.mechanic.loot.Loot; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import net.momirealms.customfishing.api.mechanic.misc.value.TextValue; +import net.momirealms.customfishing.api.mechanic.requirement.Requirement; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager; +import net.momirealms.customfishing.api.mechanic.statistic.StatisticsKeys; +import net.momirealms.customfishing.api.mechanic.totem.TotemModel; +import net.momirealms.customfishing.api.mechanic.totem.TotemParticle; +import net.momirealms.customfishing.api.mechanic.totem.block.TotemBlock; +import net.momirealms.customfishing.api.mechanic.totem.block.property.AxisImpl; +import net.momirealms.customfishing.api.mechanic.totem.block.property.FaceImpl; +import net.momirealms.customfishing.api.mechanic.totem.block.property.HalfImpl; +import net.momirealms.customfishing.api.mechanic.totem.block.property.TotemBlockProperty; +import net.momirealms.customfishing.api.mechanic.totem.block.type.TypeCondition; +import net.momirealms.customfishing.api.util.OffsetUtils; +import net.momirealms.customfishing.bukkit.item.damage.CustomDurabilityItem; +import net.momirealms.customfishing.bukkit.totem.particle.DustParticleSetting; +import net.momirealms.customfishing.bukkit.totem.particle.ParticleSetting; +import net.momirealms.customfishing.bukkit.util.ItemStackUtils; +import net.momirealms.customfishing.common.dependency.DependencyProperties; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import net.momirealms.customfishing.common.item.AbstractItem; +import net.momirealms.customfishing.common.item.Item; +import net.momirealms.customfishing.common.util.*; +import org.bukkit.*; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.Bisected; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.EventPriority; +import org.bukkit.inventory.ItemStack; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; + +public class BukkitConfigManager extends ConfigManager { + + private static YamlDocument MAIN_CONFIG; + + public static YamlDocument getMainConfig() { + return MAIN_CONFIG; + } + + public BukkitConfigManager(BukkitCustomFishingPlugin plugin) { + super(plugin); + this.registerBuiltInItemProperties(); + this.registerBuiltInBaseEffectParser(); + this.registerBuiltInLootParser(); + this.registerBuiltInEntityParser(); + this.registerBuiltInEventParser(); + this.registerBuiltInEffectModifierParser(); + this.registerBuiltInTotemParser(); + this.registerBuiltInHookParser(); + } + + @Override + public void load() { + String configVersion = DependencyProperties.getDependencyVersion("config"); + try (InputStream inputStream = new FileInputStream(resolveConfig("config.yml").toFile())) { + MAIN_CONFIG = YamlDocument.create( + inputStream, + plugin.getResourceStream("config.yml"), + GeneralSettings.builder() + .setRouteSeparator('.') + .setUseDefaults(false) + .build(), + LoaderSettings + .builder() + .setAutoUpdate(true) + .build(), + DumperSettings.DEFAULT, + UpdaterSettings + .builder() + .setVersioning(new BasicVersioning("config-version")) + .addIgnoredRoute(configVersion, "mechanics.mechanic-requirements", '.') + .addIgnoredRoute(configVersion, "mechanics.skip-game-requirements", '.') + .addIgnoredRoute(configVersion, "mechanics.auto-fishing-requirements", '.') + .addIgnoredRoute(configVersion, "mechanics.global-events", '.') + .addIgnoredRoute(configVersion, "mechanics.global-effects", '.') + .addIgnoredRoute(configVersion, "mechanics.fishing-bag.collect-requirements", '.') + .addIgnoredRoute(configVersion, "mechanics.fishing-bag.collect-actions", '.') + .addIgnoredRoute(configVersion, "mechanics.fishing-bag.full-actions", '.') + .addIgnoredRoute(configVersion, "mechanics.market.item-price", '.') + .addIgnoredRoute(configVersion, "mechanics.market.sell-all-icons", '.') + .addIgnoredRoute(configVersion, "mechanics.market.sell-icons", '.') + .addIgnoredRoute(configVersion, "mechanics.market.decorative-icons", '.') + .addIgnoredRoute(configVersion, "other-settings.placeholder-register", '.') + .build() + ); + } catch (IOException e) { + throw new RuntimeException(e); + } + this.loadSettings(); + this.loadConfigs(); + } + + private void loadSettings() { + YamlDocument config = getMainConfig(); + + metrics = config.getBoolean("metrics", true); + checkUpdate = config.getBoolean("update-checker", true); + debug = config.getBoolean("debug", false); + + overrideVanillaWaitTime = config.getBoolean("mechanics.fishing-wait-time.override-vanilla", false); + waterMinTime = config.getInt("mechanics.fishing-wait-time.min-wait-time", 100); + waterMaxTime = config.getInt("mechanics.fishing-wait-time.max-wait-time", 600); + + enableLavaFishing = config.getBoolean("mechanics.lava-fishing.enable", false); + lavaMinTime = config.getInt("mechanics.lava-fishing.min-wait-time", 100); + lavaMaxTime = config.getInt("mechanics.lava-fishing.max-wait-time", 600); + + enableVoidFishing = config.getBoolean("mechanics.void-fishing.enable", false); + voidMinTime = config.getInt("mechanics.void-fishing.min-wait-time", 100); + voidMaxTime = config.getInt("mechanics.void-fishing.max-wait-time", 600); + + restrictedSizeRange = config.getBoolean("mechanics.size.restricted-size-range", true); + + placeholderLimit = config.getInt("mechanics.competition.placeholder-limit", 3); + serverGroup = config.getString("mechanics.competition.server-group", "default"); + redisRanking = config.getBoolean("mechanics.competition.redis-ranking", false); + + AdventureHelper.legacySupport = config.getBoolean("other-settings.legacy-color-code-support", true); + dataSaveInterval = config.getInt("other-settings.data-save-interval", 600); + logDataSaving = config.getBoolean("other-settings.log-data-saving", true); + lockData = config.getBoolean("other-settings.lock-data", true); + + durabilityLore = new ArrayList<>(config.getStringList("other-settings.custom-durability-format").stream().map(it -> "" + it).toList()); + + itemDetectOrder = config.getStringList("other-settings.item-detection-order").toArray(new String[0]); + blockDetectOrder = config.getStringList("other-settings.block-detection-order").toArray(new String[0]); + + allowMultipleTotemType = config.getBoolean("mechanics.totem.allow-multiple-type", true); + allowSameTotemType = config.getBoolean("mechanics.totem.allow-same-type", false); + + eventPriority = EventPriority.valueOf(config.getString("other-settings.event-priority", "NORMAL").toUpperCase(Locale.ENGLISH)); + + mechanicRequirements = plugin.getRequirementManager().parseRequirements(config.getSection("mechanics.mechanic-requirements"), true); + skipGameRequirements = plugin.getRequirementManager().parseRequirements(config.getSection("mechanics.skip-game-requirements"), true); + autoFishingRequirements = plugin.getRequirementManager().parseRequirements(config.getSection("mechanics.auto-fishing-requirements"), true); + + enableBag = config.getBoolean("mechanics.fishing-bag.enable", true); + + baitAnimation = config.getBoolean("mechanics.bait-animation", true); + + multipleLootSpawnDelay = config.getInt("mechanics.multiple-loot-spawn-delay", 4); + + Loot.DefaultProperties.DEFAULT_DISABLE_GAME = config.getBoolean("mechanics.global-loot-property.disable-game", false); + Loot.DefaultProperties.DEFAULT_DISABLE_STATS = config.getBoolean("mechanics.global-loot-property.disable-stat", false); + Loot.DefaultProperties.DEFAULT_INSTANT_GAME = config.getBoolean("mechanics.global-loot-property.instant-game", false); + Loot.DefaultProperties.DEFAULT_SHOW_IN_FINDER = config.getBoolean("mechanics.global-loot-property.show-in-fishfinder", true); + + Section placeholderSection = config.getSection("other-settings.placeholder-register"); + if (placeholderSection != null) { + for (Map.Entry entry : placeholderSection.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof String original) { + plugin.getPlaceholderManager().registerCustomPlaceholder(entry.getKey(), original); + } + } + } + + OffsetUtils.load(config.getSection("other-settings.offset-characters")); + + globalEffects = new ArrayList<>(); + Section globalEffectSection = config.getSection("mechanics.global-effects"); + if (globalEffectSection != null) { + for (Map.Entry entry : globalEffectSection.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + globalEffects.add(parseEffect(innerSection)); + } + } + } + + EventManager.GLOBAL_ACTIONS.clear(); + EventManager.GLOBAL_TIMES_ACTION.clear(); + Section globalEvents = config.getSection("mechanics.global-events"); + if (globalEvents != null) { + for (Map.Entry entry : globalEvents.getStringRouteMappedValues(false).entrySet()) { + MechanicType type = MechanicType.index().value(entry.getKey()); + if (entry.getValue() instanceof Section inner) { + Map[]> actionMap = new HashMap<>(); + for (Map.Entry innerEntry : inner.getStringRouteMappedValues(false).entrySet()) { + if (innerEntry.getValue() instanceof Section actionSection) { + actionMap.put(ActionTrigger.valueOf(innerEntry.getKey().toUpperCase(Locale.ENGLISH)), plugin.getActionManager().parseActions(actionSection)); + } + } + EventManager.GLOBAL_ACTIONS.put(type, actionMap); + } + } + } + } + + private void loadConfigs() { + Deque fileDeque = new ArrayDeque<>(); + for (ConfigType type : ConfigType.values()) { + File typeFolder = new File(plugin.getDataFolder(), "contents" + File.separator + type.path()); + if (!typeFolder.exists()) { + if (!typeFolder.mkdirs()) return; + plugin.getBoostrap().saveResource("contents" + File.separator + type.path() + File.separator + "default.yml", false); + } + fileDeque.push(typeFolder); + while (!fileDeque.isEmpty()) { + File file = fileDeque.pop(); + File[] files = file.listFiles(); + if (files == null) continue; + for (File subFile : files) { + if (subFile.isDirectory()) { + fileDeque.push(subFile); + } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { + YamlDocument document = plugin.getConfigManager().loadData(subFile); + for (Map.Entry entry : document.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section section) { + type.parse(entry.getKey(), section, formatFunctions); + } + } + } + } + } + } + } + + private Map getEnchantments(Section section) { + Map map = new HashMap<>(); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + int level = Math.min(255, Math.max(1, (int) entry.getValue())); + if (Registry.ENCHANTMENT.get(Objects.requireNonNull(NamespacedKey.fromString(entry.getKey()))) != null) { + map.put(Key.fromString(entry.getKey()), (short) level); + } + } + return map; + } + + private List> getPossibleEnchantments(Section section) { + List> list = new ArrayList<>(); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + Tuple tuple = Tuple.of( + inner.getDouble("chance"), + inner.getString("enchant"), + Short.valueOf(String.valueOf(inner.getInt("level"))) + ); + list.add(tuple); + } + } + return list; + } + + private Pair getEnchantmentPair(String enchantmentWithLevel) { + String[] split = enchantmentWithLevel.split(":", 3); + return Pair.of(Key.of(split[0], split[1]), Short.parseShort(split[2])); + } + + private void registerBuiltInItemProperties() { + Function, Context>> f1 = arg -> { + Section section = (Section) arg; + boolean stored = Objects.equals(section.getNameAsString(), "stored-enchantment-pool"); + Section amountSection = section.getSection("amount"); + Section enchantSection = section.getSection("pool"); + List>> amountList = new ArrayList<>(); + for (Map.Entry entry : amountSection.getStringRouteMappedValues(false).entrySet()) { + amountList.add(Pair.of(Integer.parseInt(entry.getKey()), MathValue.auto(entry.getValue()))); + } + List, MathValue>> enchantPoolPair = new ArrayList<>(); + for (Map.Entry entry : enchantSection.getStringRouteMappedValues(false).entrySet()) { + enchantPoolPair.add(Pair.of(getEnchantmentPair(entry.getKey()), MathValue.auto(entry.getValue()))); + } + if (amountList.isEmpty() || enchantPoolPair.isEmpty()) { + throw new RuntimeException("Both `pool` and `amount` should not be empty"); + } + return (item, context) -> { + List> parsedAmountPair = new ArrayList<>(amountList.size()); + for (Pair> rawValue : amountList) { + parsedAmountPair.add(Pair.of(rawValue.left(), rawValue.right().evaluate(context))); + } + int amount = WeightUtils.getRandom(parsedAmountPair); + if (amount <= 0) return; + HashSet addedEnchantments = new HashSet<>(); + List, Double>> cloned = new ArrayList<>(enchantPoolPair.size()); + for (Pair, MathValue> rawValue : enchantPoolPair) { + cloned.add(Pair.of(rawValue.left(), rawValue.right().evaluate(context))); + } + int i = 0; + outer: + while (i < amount && !cloned.isEmpty()) { + Pair enchantPair = WeightUtils.getRandom(cloned); + Enchantment enchantment = Registry.ENCHANTMENT.get(Objects.requireNonNull(NamespacedKey.fromString(enchantPair.left().toString()))); + if (enchantment == null) { + plugin.getPluginLogger().warn("Enchantment: " + enchantPair.left() + " doesn't exist."); + return; + } + if (!stored) { + for (Enchantment added : addedEnchantments) { + if (enchantment.conflictsWith(added)) { + cloned.removeIf(pair -> pair.left().left().equals(enchantPair.left())); + continue outer; + } + } + } + if (stored) { + item.addStoredEnchantment(enchantPair.left(), enchantPair.right()); + } else { + item.addEnchantment(enchantPair.left(), enchantPair.right()); + } + addedEnchantments.add(enchantment); + cloned.removeIf(pair -> pair.left().left().equals(enchantPair.left())); + i++; + } + }; + }; + this.registerItemParser(f1, 4800, "stored-enchantment-pool"); + this.registerItemParser(f1, 4700, "enchantment-pool"); + Function, Context>> f2 = arg -> { + Section section = (Section) arg; + boolean stored = Objects.equals(section.getNameAsString(), "stored-random-enchantments"); + List> enchantments = getPossibleEnchantments(section); + return (item, context) -> { + HashSet ids = new HashSet<>(); + for (Tuple pair : enchantments) { + if (Math.random() < pair.left() && !ids.contains(pair.mid())) { + if (stored) { + item.addStoredEnchantment(Key.fromString(pair.mid()), pair.right()); + } else { + item.addEnchantment(Key.fromString(pair.mid()), pair.right()); + } + ids.add(pair.mid()); + } + } + }; + }; + this.registerItemParser(f2, 4850, "random-stored-enchantments"); + this.registerItemParser(f2, 4750, "random-enchantments"); + this.registerItemParser(arg -> { + Section section = (Section) arg; + Map map = getEnchantments(section); + return (item, context) -> item.storedEnchantments(map); + }, 4600, "stored-enchantments"); + this.registerItemParser(arg -> { + Section section = (Section) arg; + Map map = getEnchantments(section); + return (item, context) -> item.enchantments(map); + }, 4500, "enchantments"); + this.registerItemParser(arg -> { + String base64 = (String) arg; + return (item, context) -> item.skull(base64); + }, 5200, "head"); + this.registerItemParser(arg -> { + List args = ListUtils.toList(arg); + return (item, context) -> item.itemFlags(args); + }, 5100, "item-flags"); + this.registerItemParser(arg -> { + MathValue mathValue = MathValue.auto(arg); + return (item, context) -> item.customModelData((int) mathValue.evaluate(context)); + }, 5000, "custom-model-data"); + this.registerItemParser(arg -> { + TextValue textValue = TextValue.auto("" + arg); + return (item, context) -> { + item.displayName(AdventureHelper.miniMessageToJson(textValue.render(context))); + }; + }, 4000, "display", "name"); + this.registerItemParser(arg -> { + List list = ListUtils.toList(arg); + List> lore = new ArrayList<>(); + for (String text : list) { + lore.add(TextValue.auto("" + text)); + } + return (item, context) -> { + item.lore(lore.stream() + .map(it -> AdventureHelper.miniMessageToJson(it.render(context))) + .toList()); + }; + }, 3_000, "display", "lore"); + this.registerItemParser(arg -> { + boolean enable = (boolean) arg; + return (item, context) -> { + if (!enable) return; + item.setTag(context.arg(ContextKeys.ID), "CustomFishing", "id"); + }; + }, 2_000, "tag"); + this.registerItemParser(arg -> { + boolean enable = (boolean) arg; + return (item, context) -> { + if (enable) return; + item.setTag(UUID.randomUUID(), "CustomFishing", "uuid"); + }; + }, 2_222, "stackable"); + this.registerItemParser(arg -> { + String sizePair = (String) arg; + String[] split = sizePair.split("~", 2); + MathValue min = MathValue.auto(split[0]); + MathValue max = MathValue.auto(split[1]); + return (item, context) -> { + double minSize = min.evaluate(context); + double maxSize = max.evaluate(context); + float size = (float) RandomUtils.generateRandomDouble(minSize, maxSize); + item.setTag(size, "CustomFishing", "size"); + context.arg(ContextKeys.SIZE, size); + context.arg(ContextKeys.SIZE_FORMATTED, String.format("%.2f", size)); + }; + }, 1_000, "size"); + this.registerItemParser(arg -> { + Section section = (Section) arg; + MathValue base = MathValue.auto(section.get("base", "0")); + MathValue bonus = MathValue.auto(section.get("bonus", "0")); + return (item, context) -> { + double basePrice = base.evaluate(context); + double bonusPrice = bonus.evaluate(context); + float size = Optional.ofNullable(context.arg(ContextKeys.SIZE)).orElse(0f); + double price = basePrice + bonusPrice * size; + item.setTag(price, "Price"); + context.arg(ContextKeys.PRICE, price); + context.arg(ContextKeys.PRICE_FORMATTED, String.format("%.2f", price)); + }; + }, 1_500, "price"); + this.registerItemParser(arg -> { + boolean random = (boolean) arg; + return (item, context) -> { + if (!random) return; + if (item.hasTag("CustomFishing", "max_dur")) { + CustomDurabilityItem durabilityItem = new CustomDurabilityItem(item); + durabilityItem.damage(RandomUtils.generateRandomInt(0, durabilityItem.maxDamage() - 1)); + } else { + item.damage(RandomUtils.generateRandomInt(0, item.maxDamage().get() - 1)); + } + }; + }, 3200, "random-durability"); + this.registerItemParser(arg -> { + MathValue mathValue = MathValue.auto(arg); + return (item, context) -> { + int max = (int) mathValue.evaluate(context); + item.setTag(max, "CustomFishing", "max_dur"); + item.setTag(max, "CustomFishing", "cur_dur"); + CustomDurabilityItem customDurabilityItem = new CustomDurabilityItem(item); + customDurabilityItem.damage(0); + }; + }, 3100, "max-durability"); + this.registerItemParser(arg -> { + Section section = (Section) arg; + ArrayList editors = new ArrayList<>(); + ItemStackUtils.sectionToTagEditor(section, editors); + return (item, context) -> { + for (ItemEditor editor : editors) { + editor.apply(((AbstractItem) item).getRTagItem(), context); + } + }; + }, 10_050, "nbt"); + this.registerItemParser(arg -> { + Section section = (Section) arg; + ArrayList editors = new ArrayList<>(); + ItemStackUtils.sectionToComponentEditor(section, editors); + return (item, context) -> { + for (ItemEditor editor : editors) { + editor.apply(((AbstractItem) item).getRTagItem(), context); + } + }; + }, 10_075, "components"); + } + + private void registerBuiltInEffectModifierParser() { + this.registerEffectModifierParser(object -> { + Section section = (Section) object; + return builder -> builder.requirements(List.of(plugin.getRequirementManager().parseRequirements(section, true))); + }, "requirements"); + this.registerEffectModifierParser(object -> { + Section section = (Section) object; + ArrayList, Integer>> property = new ArrayList<>(); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + property.add(parseEffect(innerSection)); + } + } + return builder -> { + builder.modifiers(property); + }; + }, "effects"); + } + + private TriConsumer, Integer> parseEffect(Section section) { + switch (section.getString("type")) { + case "lava-fishing" -> { + return (((effect, context, phase) -> { + if (phase == 0) effect.properties().put(EffectProperties.LAVA_FISHING, true); + })); + } + case "void-fishing" -> { + return (((effect, context, phase) -> { + if (phase == 0) effect.properties().put(EffectProperties.VOID_FISHING, true); + })); + } + case "weight-mod" -> { + var op = parseWeightOperation(section.getStringList("value")); + return (((effect, context, phase) -> { + if (phase == 1) effect.weightOperations(op); + })); + } + case "weight-mod-ignore-conditions" -> { + var op = parseWeightOperation(section.getStringList("value")); + return (((effect, context, phase) -> { + if (phase == 1) effect.weightOperationsIgnored(op); + })); + } + case "group-mod" -> { + var op = parseGroupWeightOperation(section.getStringList("value")); + return (((effect, context, phase) -> { + if (phase == 1) effect.weightOperations(op); + })); + } + case "group-mod-ignore-conditions" -> { + var op = parseGroupWeightOperation(section.getStringList("value")); + return (((effect, context, phase) -> { + if (phase == 1) effect.weightOperationsIgnored(op); + })); + } + case "wait-time" -> { + MathValue value = MathValue.auto(section.get("value")); + return (((effect, context, phase) -> { + if (phase == 2) effect.waitTimeAdder(effect.waitTimeAdder() + value.evaluate(context)); + })); + } + case "hook-time", "wait-time-multiplier" -> { + MathValue value = MathValue.auto(section.get("value")); + return (((effect, context, phase) -> { + if (phase == 2) effect.waitTimeMultiplier(effect.waitTimeMultiplier() - 1 + value.evaluate(context)); + })); + } + case "difficulty" -> { + MathValue value = MathValue.auto(section.get("value")); + return (((effect, context, phase) -> { + if (phase == 2) effect.difficultyAdder(effect.difficultyAdder() + value.evaluate(context)); + })); + } + case "difficulty-multiplier", "difficulty-bonus" -> { + MathValue value = MathValue.auto(section.get("value")); + return (((effect, context, phase) -> { + if (phase == 2) effect.difficultyMultiplier(effect.difficultyMultiplier() - 1 + value.evaluate(context)); + })); + } + case "size" -> { + MathValue value = MathValue.auto(section.get("value")); + return (((effect, context, phase) -> { + if (phase == 2) effect.sizeAdder(effect.sizeAdder() + value.evaluate(context)); + })); + } + case "size-multiplier", "size-bonus" -> { + MathValue value = MathValue.auto(section.get("value")); + return (((effect, context, phase) -> { + if (phase == 2) effect.sizeMultiplier(effect.sizeMultiplier() - 1 + value.evaluate(context)); + })); + } + case "game-time" -> { + MathValue value = MathValue.auto(section.get("value")); + return (((effect, context, phase) -> { + if (phase == 2) effect.gameTimeAdder(effect.gameTimeAdder() + value.evaluate(context)); + })); + } + case "game-time-multiplier", "game-time-bonus" -> { + MathValue value = MathValue.auto(section.get("value")); + return (((effect, context, phase) -> { + if (phase == 2) effect.gameTimeMultiplier(effect.gameTimeMultiplier() - 1 + value.evaluate(context)); + })); + } + case "score" -> { + MathValue value = MathValue.auto(section.get("value")); + return (((effect, context, phase) -> { + if (phase == 2) effect.scoreAdder(effect.scoreAdder() + value.evaluate(context)); + })); + } + case "score-multiplier", "score-bonus" -> { + MathValue value = MathValue.auto(section.get("value")); + return (((effect, context, phase) -> { + if (phase == 2) effect.scoreMultiplier(effect.scoreMultiplier() - 1 + value.evaluate(context)); + })); + } + case "multiple-loot" -> { + MathValue value = MathValue.auto(section.get("value")); + return (((effect, context, phase) -> { + if (phase == 2) effect.multipleLootChance(effect.multipleLootChance() + value.evaluate(context)); + })); + } + case "conditional" -> { + Requirement[] requirements = plugin.getRequirementManager().parseRequirements(section.getSection("conditions"), true); + Section effectSection = section.getSection("effects"); + ArrayList, Integer>> effects = new ArrayList<>(); + if (effectSection != null) + for (Map.Entry entry : effectSection.getStringRouteMappedValues(false).entrySet()) + if (entry.getValue() instanceof Section inner) + effects.add(parseEffect(inner)); + return (((effect, context, phase) -> { + if (!RequirementManager.isSatisfied(context, requirements)) return; + for (TriConsumer, Integer> consumer : effects) { + consumer.accept(effect, context, phase); + } + })); + } + default -> { + return (((effect, context, phase) -> {})); + } + } + } + + private BiFunction, Double, Double> parseWeightOperation(String op) { + switch (op.charAt(0)) { + case '/' -> { + MathValue arg = MathValue.auto(op.substring(1)); + return (context, weight) -> weight / arg.evaluate(context); + } + case '*' -> { + MathValue arg = MathValue.auto(op.substring(1)); + return (context, weight) -> weight * arg.evaluate(context); + } + case '-' -> { + MathValue arg = MathValue.auto(op.substring(1)); + return (context, weight) -> weight - arg.evaluate(context); + } + case '%' -> { + MathValue arg = MathValue.auto(op.substring(1)); + return (context, weight) -> weight % arg.evaluate(context); + } + case '+' -> { + MathValue arg = MathValue.auto(op.substring(1)); + return (context, weight) -> weight + arg.evaluate(context); + } + case '=' -> { + MathValue arg = MathValue.auto(op.substring(1)); + return (context, weight) -> { + context.arg(ContextKeys.WEIGHT, weight); + return arg.evaluate(context); + }; + } + default -> throw new IllegalArgumentException("Invalid weight operation: " + op); + } + } + + @Override + public List, Double, Double>>> parseWeightOperation(List ops) { + List, Double, Double>>> result = new ArrayList<>(); + for (String op : ops) { + String[] split = op.split(":", 2); + result.add(Pair.of(split[0], parseWeightOperation(split[1]))); + } + return result; + } + + @Override + public List, Double, Double>>> parseGroupWeightOperation(List gops) { + List, Double, Double>>> result = new ArrayList<>(); + for (String gop : gops) { + String[] split = gop.split(":", 2); + BiFunction, Double, Double> operation = parseWeightOperation(split[1]); + for (String member : plugin.getLootManager().getGroupMembers(split[0])) { + result.add(Pair.of(member, operation)); + } + } + return result; + } + + private void registerBuiltInHookParser() { + this.registerHookParser(object -> { + List lore = ListUtils.toList(object); + return builder -> builder.lore(lore.stream().map(it -> "" + it).toList()); + }, "lore-on-rod"); + } + + private void registerBuiltInTotemParser() { + this.registerTotemParser(object -> { + MathValue mathValue = MathValue.auto(object); + return builder -> builder.radius(mathValue); + }, "radius"); + this.registerTotemParser(object -> { + MathValue mathValue = MathValue.auto(object); + return builder -> builder.duration(mathValue); + }, "duration"); + this.registerTotemParser(object -> { + Section section = (Section) object; + TotemParticle[] particles = getParticleSettings(section); + return builder -> builder.particleSettings(particles); + }, "particles"); + this.registerTotemParser(object -> { + Section section = (Section) object; + TotemModel[] models = getTotemModels(section); + return builder -> builder.totemModels(models); + }, "pattern"); + } + + private void registerBuiltInBaseEffectParser() { + this.registerBaseEffectParser(object -> { + MathValue mathValue = MathValue.auto(object); + return builder -> builder.difficultyAdder(mathValue); + }, "base-effects", "difficulty-adder"); + this.registerBaseEffectParser(object -> { + MathValue mathValue = MathValue.auto(object); + return builder -> builder.difficultyMultiplier(mathValue); + }, "base-effects", "difficulty-multiplier"); + this.registerBaseEffectParser(object -> { + MathValue mathValue = MathValue.auto(object); + return builder -> builder.gameTimeAdder(mathValue); + }, "base-effects", "game-time-adder"); + this.registerBaseEffectParser(object -> { + MathValue mathValue = MathValue.auto(object); + return builder -> builder.gameTimeMultiplier(mathValue); + }, "base-effects", "game-time-multiplier"); + this.registerBaseEffectParser(object -> { + MathValue mathValue = MathValue.auto(object); + return builder -> builder.waitTimeAdder(mathValue); + }, "base-effects", "wait-time-adder"); + this.registerBaseEffectParser(object -> { + MathValue mathValue = MathValue.auto(object); + return builder -> builder.waitTimeMultiplier(mathValue); + }, "base-effects", "wait-time-multiplier"); + } + + private void registerBuiltInEntityParser() { + this.registerEntityParser(object -> { + String entity = (String) object; + return builder -> builder.entityID(entity); + }, "entity"); + this.registerEntityParser(object -> { + MathValue mathValue = MathValue.auto(object); + return builder -> builder.horizontalVector(mathValue); + }, "velocity", "horizontal"); + this.registerEntityParser(object -> { + MathValue mathValue = MathValue.auto(object); + return builder -> builder.verticalVector(mathValue); + }, "velocity", "vertical"); + this.registerEntityParser(object -> { + Section section = (Section) object; + return builder -> builder.propertyMap(section.getStringRouteMappedValues(false)); + }, "properties"); + } + + private void registerBuiltInEventParser() { + this.registerEventParser(object -> { + boolean disable = (boolean) object; + return builder -> builder.disableGlobalActions(disable); + }, "disable-global-event"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.LURE, actions); + }, "events", "lure"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.ESCAPE, actions); + }, "events", "escape"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.SUCCESS, actions); + }, "events", "success"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.ACTIVATE, actions); + }, "events", "activate"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.FAILURE, actions); + }, "events", "failure"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.HOOK, actions); + }, "events", "hook"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.CONSUME, actions); + }, "events", "consume"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.CAST, actions); + }, "events", "cast"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.BITE, actions); + }, "events", "bite"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.LAND, actions); + }, "events", "land"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.TIMER, actions); + }, "events", "timer"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.INTERACT, actions); + }, "events", "interact"); + this.registerEventParser(object -> { + Section section = (Section) object; + Action[] actions = plugin.getActionManager().parseActions(section); + return builder -> builder.action(ActionTrigger.NEW_SIZE_RECORD, actions); + }, "events", "new_size_record"); + } + + private void registerBuiltInLootParser() { + this.registerLootParser(object -> { + boolean value = (boolean) object; + return builder -> builder.preventGrabbing(value); + }, "prevent-grabbing"); + this.registerLootParser(object -> { + String string = (String) object; + return builder -> builder.nick(string); + }, "nick"); + this.registerLootParser(object -> { + boolean value = (boolean) object; + return builder -> builder.showInFinder(value); + }, "show-in-fishfinder"); + this.registerLootParser(object -> { + boolean value = (boolean) object; + return builder -> builder.disableStatistics(value); + }, "disable-stat"); + this.registerLootParser(object -> { + boolean value = (boolean) object; + return builder -> builder.disableGame(value); + }, "disable-game"); + this.registerLootParser(object -> { + boolean value = (boolean) object; + return builder -> builder.instantGame(value); + }, "instant-game"); + this.registerLootParser(object -> { + MathValue mathValue = MathValue.auto(object); + return builder -> builder.score(mathValue); + }, "score"); + this.registerLootParser(object -> { + List args = ListUtils.toList(object); + return builder -> builder.groups(args.toArray(new String[0])); + }, "group"); + this.registerLootParser(object -> { + Section section = (Section) object; + StatisticsKeys keys = new StatisticsKeys( + section.getString("amount"), + section.getString("size") + ); + return builder -> builder.statisticsKeys(keys); + }, "statistics"); + } + + @Override + public void saveResource(String filePath) { + if (!new File(plugin.getDataFolder(), filePath).exists()) { + plugin.getBoostrap().saveResource(filePath, false); + } + } + + private ParticleSetting[] getParticleSettings(Section section) { + List particleSettings = new ArrayList<>(); + if (section != null) + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + particleSettings.add(getParticleSetting(innerSection)); + } + } + return particleSettings.toArray(new ParticleSetting[0]); + } + + private ParticleSetting getParticleSetting(Section section) { + Particle particle = Particle.valueOf(section.getString("type","REDSTONE")); + String formulaHorizontal = section.getString("polar-coordinates-formula.horizontal"); + String formulaVertical = section.getString("polar-coordinates-formula.vertical"); + List> ranges = section.getStringList("theta.range") + .stream().map(it -> { + String[] split = it.split("~"); + return Pair.of(Double.parseDouble(split[0]) * Math.PI / 180, Double.parseDouble(split[1]) * Math.PI / 180); + }).toList(); + + double interval = section.getDouble("theta.draw-interval", 3d); + int delay = section.getInt("task.delay", 0); + int period = section.getInt("task.period", 0); + if (particle == Particle.REDSTONE) { + String color = section.getString("options.color","0,0,0"); + String[] colorSplit = color.split(","); + return new DustParticleSetting( + formulaHorizontal, + formulaVertical, + particle, + interval, + ranges, + delay, + period, + new Particle.DustOptions( + Color.fromRGB( + Integer.parseInt(colorSplit[0]), + Integer.parseInt(colorSplit[1]), + Integer.parseInt(colorSplit[2]) + ), + section.getDouble("options.scale", 1.0).floatValue() + ) + ); + } else if (particle == Particle.DUST_COLOR_TRANSITION) { + String color = section.getString("options.from","0,0,0"); + String[] colorSplit = color.split(","); + String toColor = section.getString("options.to","255,255,255"); + String[] toColorSplit = toColor.split(","); + return new DustParticleSetting( + formulaHorizontal, + formulaVertical, + particle, + interval, + ranges, + delay, + period, + new Particle.DustTransition( + Color.fromRGB( + Integer.parseInt(colorSplit[0]), + Integer.parseInt(colorSplit[1]), + Integer.parseInt(colorSplit[2]) + ), + Color.fromRGB( + Integer.parseInt(toColorSplit[0]), + Integer.parseInt(toColorSplit[1]), + Integer.parseInt(toColorSplit[2]) + ), + section.getDouble("options.scale", 1.0).floatValue() + ) + ); + } else { + return new ParticleSetting( + formulaHorizontal, + formulaVertical, + particle, + interval, + ranges, + delay, + period + ); + } + } + + private TotemModel[] getTotemModels(Section section) { + TotemModel originalModel = parseModel(section); + List modelList = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + originalModel = originalModel.deepClone().rotate90(); + modelList.add(originalModel); + if (i % 2 == 0) { + modelList.add(originalModel.mirrorVertically()); + } else { + modelList.add(originalModel.mirrorHorizontally()); + } + } + return modelList.toArray(new TotemModel[0]); + } + + @SuppressWarnings("unchecked") + private TotemModel parseModel(Section section) { + Section layerSection = section.getSection("layer"); + List totemBlocksList = new ArrayList<>(); + if (layerSection != null) { + var set = layerSection.getStringRouteMappedValues(false).entrySet(); + TotemBlock[][][][] totemBlocks = new TotemBlock[set.size()][][][]; + for (Map.Entry entry : set) { + if (entry.getValue() instanceof List list) { + totemBlocks[Integer.parseInt(entry.getKey())-1] = parseLayer((List) list); + } + } + totemBlocksList.addAll(List.of(totemBlocks)); + } + + String[] core = section.getString("core","1,1,1").split(","); + int x = Integer.parseInt(core[2]) - 1; + int z = Integer.parseInt(core[1]) - 1; + int y = Integer.parseInt(core[0]) - 1; + return new TotemModel( + x,y,z, + totemBlocksList.toArray(new TotemBlock[0][][][]) + ); + } + + private TotemBlock[][][] parseLayer(List lines) { + List totemBlocksList = new ArrayList<>(); + for (String line : lines) { + totemBlocksList.add(parseSingleLine(line)); + } + return totemBlocksList.toArray(new TotemBlock[0][][]); + } + + private TotemBlock[][] parseSingleLine(String line) { + List totemBlocksList = new ArrayList<>(); + String[] splits = line.split("\\s+"); + for (String split : splits) { + totemBlocksList.add(parseSingleElement(split)); + } + return totemBlocksList.toArray(new TotemBlock[0][]); + } + + private TotemBlock[] parseSingleElement(String element) { + String[] orBlocks = element.split("\\|\\|"); + List totemBlockList = new ArrayList<>(); + for (String block : orBlocks) { + int index = block.indexOf("{"); + List propertyList = new ArrayList<>(); + if (index == -1) { + index = block.length(); + } else { + String propertyStr = block.substring(index+1, block.length()-1); + String[] properties = propertyStr.split(";"); + for (String property : properties) { + String[] split = property.split("="); + if (split.length < 2) continue; + String key = split[0]; + String value = split[1]; + switch (key) { + // Block face + case "face" -> { + BlockFace blockFace = BlockFace.valueOf(value.toUpperCase(Locale.ENGLISH)); + propertyList.add(new FaceImpl(blockFace)); + } + // Block axis + case "axis" -> { + Axis axis = Axis.valueOf(value.toUpperCase(Locale.ENGLISH)); + propertyList.add(new AxisImpl(axis)); + } + // Slab, Stair half + case "half" -> { + Bisected.Half half = Bisected.Half.valueOf(value.toUpperCase(Locale.ENGLISH)); + propertyList.add(new HalfImpl(half)); + } + } + } + } + String type = block.substring(0, index); + TotemBlock totemBlock = new TotemBlock( + TypeCondition.getTypeCondition(type), + propertyList.toArray(new TotemBlockProperty[0]) + ); + totemBlockList.add(totemBlock); + } + return totemBlockList.toArray(new TotemBlock[0]); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/effect/BukkitEffectManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/effect/BukkitEffectManager.java new file mode 100644 index 00000000..27e29a71 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/effect/BukkitEffectManager.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.effect; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.effect.EffectManager; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; + +import java.util.HashMap; +import java.util.Optional; + +public class BukkitEffectManager implements EffectManager { + + private final BukkitCustomFishingPlugin plugin; + private final HashMap effectModifiers = new HashMap<>(); + + public BukkitEffectManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void unload() { + this.effectModifiers.clear(); + } + + @Override + public void load() { + plugin.debug("Loaded " + effectModifiers.size() + " effects"); + } + + @Override + public boolean registerEffectModifier(EffectModifier effect, MechanicType type) { + if (effectModifiers.containsKey(effect.id())) return false; + this.effectModifiers.put(type.getType() + ":" + effect.id(), effect); + return true; + } + + @Override + public Optional getEffectModifier(String id, MechanicType type) { + return Optional.ofNullable(this.effectModifiers.get(type.getType() + ":" + id)); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/entity/BukkitEntityManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/entity/BukkitEntityManager.java new file mode 100644 index 00000000..e1478bb4 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/entity/BukkitEntityManager.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.entity; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.integration.EntityProvider; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.entity.EntityConfig; +import net.momirealms.customfishing.api.mechanic.entity.EntityManager; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class BukkitEntityManager implements EntityManager { + + private final BukkitCustomFishingPlugin plugin; + private final HashMap entityProviders = new HashMap<>(); + private final HashMap entities = new HashMap<>(); + + public BukkitEntityManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + this.registerEntityProvider(new EntityProvider() { + @Override + public String identifier() { + return "vanilla"; + } + @NotNull + @Override + public Entity spawn(@NotNull Location location, @NotNull String id, @NotNull Map propertyMap) { + return location.getWorld().spawnEntity(location, EntityType.valueOf(id.toUpperCase(Locale.ENGLISH))); + } + }); + } + + @Override + public void load() { + for (EntityProvider provider : entityProviders.values()) { + plugin.debug("Registered EntityProvider: " + provider.identifier()); + } + plugin.debug("Loaded " + entities.size() + " entities"); + } + + @Override + public void unload() { + this.entities.clear(); + } + + @Override + public void disable() { + unload(); + this.entityProviders.clear(); + } + + @Override + public Optional getEntity(String id) { + return Optional.ofNullable(this.entities.get(id)); + } + + @Override + public boolean registerEntity(EntityConfig entity) { + if (entities.containsKey(entity.id())) return false; + this.entities.put(entity.id(), entity); + return true; + } + + public boolean registerEntityProvider(EntityProvider entityProvider) { + if (entityProviders.containsKey(entityProvider.identifier())) return false; + else entityProviders.put(entityProvider.identifier(), entityProvider); + return true; + } + + public boolean unregisterEntityProvider(String id) { + return entityProviders.remove(id) != null; + } + + @NotNull + @Override + public Entity summonEntityLoot(Context context) { + String id = context.arg(ContextKeys.ID); + EntityConfig config = requireNonNull(entities.get(id), "Entity " + id + " not found"); + Location hookLocation = requireNonNull(context.arg(ContextKeys.OTHER_LOCATION)); + Location playerLocation = requireNonNull(context.getHolder().getLocation()); + String entityID = config.entityID(); + Entity entity; + if (entityID.contains(":")) { + String[] split = entityID.split(":", 2); + EntityProvider provider = requireNonNull(entityProviders.get(split[0]), "EntityProvider " + split[0] + " doesn't exist"); + entity = requireNonNull(provider.spawn(hookLocation, split[1], config.propertyMap()), "Entity " + entityID + " doesn't exist"); + } else { + entity = entityProviders.get("vanilla").spawn(hookLocation, entityID, config.propertyMap()); + } + double d0 = playerLocation.getX() - hookLocation.getX(); + double d1 = playerLocation.getY() - hookLocation.getY(); + double d2 = playerLocation.getZ() - hookLocation.getZ(); + double d3 = config.horizontalVector().evaluate(context); + double d4 = config.verticalVector().evaluate(context); + Vector vector = new Vector(d0 * 0.1D * d3, d1 * 0.1D + Math.sqrt(Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2)) * 0.08D * d4, d2 * 0.1D * d3); + entity.setVelocity(vector); + return entity; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/event/BukkitEventManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/event/BukkitEventManager.java new file mode 100644 index 00000000..ea20a7c0 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/event/BukkitEventManager.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.event; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.event.EventCarrier; +import net.momirealms.customfishing.api.mechanic.event.EventManager; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemConsumeEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.Optional; + +public class BukkitEventManager implements EventManager, Listener { + + private final HashMap carriers = new HashMap<>(); + private final BukkitCustomFishingPlugin plugin; + + public BukkitEventManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void unload() { + this.carriers.clear(); + HandlerList.unregisterAll(this); + } + + @Override + public void load() { + Bukkit.getPluginManager().registerEvents(this, this.plugin.getBoostrap()); + } + + @Override + public Optional getEventCarrier(String id, MechanicType type) { + return Optional.ofNullable(this.carriers.get(type.getType() + ":" + id)); + } + + @Override + public boolean registerEventCarrier(EventCarrier carrier) { + if (this.carriers.containsKey(carrier.id())) return false; + this.carriers.put(carrier.type().getType() + ":" + carrier.id(), carrier); + return true; + } + + @EventHandler + public void onInteract(PlayerInteractEvent event) { + if (event.getHand() != EquipmentSlot.HAND) + return; + if (event.getAction() != org.bukkit.event.block.Action.RIGHT_CLICK_AIR && event.getAction() != org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK) + return; + ItemStack itemStack = event.getPlayer().getInventory().getItemInMainHand(); + if (itemStack.getType() == Material.AIR || itemStack.getAmount() == 0) + return; + String id = this.plugin.getItemManager().getItemID(itemStack); + Context context = Context.player(event.getPlayer()); + Block clicked = event.getClickedBlock(); + context.arg(ContextKeys.OTHER_LOCATION, clicked == null ? event.getPlayer().getLocation() : clicked.getLocation()); + trigger(context, id, MechanicType.UTIL, ActionTrigger.INTERACT); + } + + @EventHandler (ignoreCancelled = true) + public void onConsumeItem(PlayerItemConsumeEvent event) { + Context context = Context.player(event.getPlayer()); + trigger(context, plugin.getItemManager().getItemID(event.getItem()), MechanicType.LOOT, ActionTrigger.CONSUME); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/fishing/BukkitFishingManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/fishing/BukkitFishingManager.java new file mode 100644 index 00000000..e1728658 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/fishing/BukkitFishingManager.java @@ -0,0 +1,354 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.fishing; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.event.FishingHookStateEvent; +import net.momirealms.customfishing.api.event.RodCastEvent; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.fishing.CustomFishingHook; +import net.momirealms.customfishing.api.mechanic.fishing.FishingGears; +import net.momirealms.customfishing.api.mechanic.fishing.FishingManager; +import net.momirealms.customfishing.api.mechanic.fishing.hook.VanillaMechanic; +import net.momirealms.customfishing.api.mechanic.game.AbstractGamingPlayer; +import net.momirealms.customfishing.api.mechanic.game.GamingPlayer; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager; +import net.momirealms.customfishing.api.util.EventUtils; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Entity; +import org.bukkit.entity.FishHook; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.*; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataType; + +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class BukkitFishingManager implements FishingManager, Listener { + + private final BukkitCustomFishingPlugin plugin; + private final ConcurrentHashMap castHooks = new ConcurrentHashMap<>(); + + public BukkitFishingManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void unload() { + HandlerList.unregisterAll(this); + } + + @Override + public void load() { + Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap()); + } + + @Override + public Optional getFishHook(Player player) { + return getFishHook(player.getUniqueId()); + } + + @Override + public Optional getFishHook(UUID player) { + return Optional.ofNullable(castHooks.get(player)); + } + + @Override + public Optional getOwner(FishHook hook) { + if (hook.getOwnerUniqueId() != null) { + return Optional.ofNullable(Bukkit.getPlayer(hook.getOwnerUniqueId())); + } + return Optional.empty(); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onFishMONITOR(PlayerFishEvent event) { + if (ConfigManager.eventPriority() != EventPriority.MONITOR) return; + this.selectState(event); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onFishHIGHEST(PlayerFishEvent event) { + if (ConfigManager.eventPriority() != EventPriority.HIGHEST) return; + this.selectState(event); + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onFishHIGH(PlayerFishEvent event) { + if (ConfigManager.eventPriority() != EventPriority.HIGH) return; + this.selectState(event); + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onFishNORMAL(PlayerFishEvent event) { + if (ConfigManager.eventPriority() != EventPriority.NORMAL) return; + this.selectState(event); + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onFishLOW(PlayerFishEvent event) { + if (ConfigManager.eventPriority() != EventPriority.LOW) return; + this.selectState(event); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onFishLOWEST(PlayerFishEvent event) { + if (ConfigManager.eventPriority() != EventPriority.LOWEST) return; + this.selectState(event); + } + + @EventHandler(ignoreCancelled = true) + public void onItemHeldChange(PlayerItemHeldEvent event) { + if (getFishHook(event.getPlayer()).isPresent()) { + this.destroy(event.getPlayer().getUniqueId()); + } + } + + @EventHandler(ignoreCancelled = true) + public void onSwapItem(PlayerSwapHandItemsEvent event) { + getFishHook(event.getPlayer()).ifPresent(hook -> { + Optional optionalGamingPlayer = hook.getGamingPlayer(); + if (optionalGamingPlayer.isPresent()) { + optionalGamingPlayer.get().handleSwapHand(); + event.setCancelled(true); + } else { + this.destroy(event.getPlayer().getUniqueId()); + } + }); + } + + @EventHandler(ignoreCancelled = true) + public void onJump(PlayerMoveEvent event) { + final Player player = event.getPlayer(); + if (event.getFrom().getY() < event.getTo().getY() && player.isOnGround()) { + getFishHook(player).flatMap(CustomFishingHook::getGamingPlayer).ifPresent(gamingPlayer -> { + if (gamingPlayer.handleJump()) { + event.setCancelled(true); + } + }); + } + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + this.destroy(event.getPlayer().getUniqueId()); + } + + @EventHandler (ignoreCancelled = false) + public void onLeftClick(PlayerInteractEvent event) { + if (event.getAction() != Action.LEFT_CLICK_AIR) + return; + if (event.getMaterial() != Material.FISHING_ROD) + return; + if (event.getHand() != EquipmentSlot.HAND) + return; + getFishHook(event.getPlayer()).ifPresent(hook -> { + Optional optionalGamingPlayer = hook.getGamingPlayer(); + if (optionalGamingPlayer.isPresent()) { + if (((AbstractGamingPlayer) optionalGamingPlayer.get()).internalLeftClick()) { + event.setCancelled(true); + } + } + }); + } + + @EventHandler (ignoreCancelled = true) + public void onSneak(PlayerToggleSneakEvent event) { + if (!event.isSneaking()) return; + getFishHook(event.getPlayer()).ifPresent(hook -> { + Optional optionalGamingPlayer = hook.getGamingPlayer(); + if (optionalGamingPlayer.isPresent()) { + if (optionalGamingPlayer.get().handleSneak()) { + event.setCancelled(true); + } + } + }); + } + + @EventHandler + public void onChat(AsyncPlayerChatEvent event) { + if (event.isCancelled()) return; + getFishHook(event.getPlayer()).ifPresent(hook -> { + Optional optionalGamingPlayer = hook.getGamingPlayer(); + if (optionalGamingPlayer.isPresent()) { + if (optionalGamingPlayer.get().handleChat(event.getMessage())) { + event.setCancelled(true); + } + } + }); + } + + private void selectState(PlayerFishEvent event) { + switch (event.getState()) { + case FISHING -> onCastRod(event); + case REEL_IN, CAUGHT_FISH -> onReelIn(event); + case CAUGHT_ENTITY -> onCaughtEntity(event); + //case CAUGHT_FISH -> onCaughtFish(event); + case BITE -> onBite(event); + case IN_GROUND -> onInGround(event); + case FAILED_ATTEMPT -> onFailedAttempt(event); + // case LURED 1.20.5+ + } + } + + // for vanilla mechanics + private void onFailedAttempt(PlayerFishEvent event) { + Player player = event.getPlayer(); + getFishHook(player).ifPresent(hook -> { + if (hook.getCurrentHookMechanic() instanceof VanillaMechanic vanillaMechanic) { + vanillaMechanic.onFailedAttempt(); + } + }); + } + + // for vanilla mechanics + private void onBite(PlayerFishEvent event) { + Player player = event.getPlayer(); + getFishHook(player).ifPresent(hook -> { + if (hook.getCurrentHookMechanic() instanceof VanillaMechanic vanillaMechanic) { + vanillaMechanic.onBite(); + } + }); + } + + private void onCaughtEntity(PlayerFishEvent event) { + final Player player = event.getPlayer(); + Optional hook = getFishHook(player); + if (hook.isPresent()) { + Entity entity = event.getCaught(); + if (entity != null && entity.getPersistentDataContainer().get( + Objects.requireNonNull(NamespacedKey.fromString("temp-entity", plugin.getBoostrap())), + PersistentDataType.STRING + ) != null) { + event.setCancelled(true); + hook.get().onReelIn(); + return; + } + } + + if (player.getGameMode() != GameMode.CREATIVE) { + ItemStack itemStack = player.getInventory().getItemInMainHand(); + if (itemStack.getType() != Material.FISHING_ROD) itemStack = player.getInventory().getItemInOffHand(); + if (plugin.getItemManager().hasCustomDurability(itemStack)) { + event.getHook().pullHookedEntity(); + event.getHook().remove(); + event.setCancelled(true); + plugin.getItemManager().decreaseDurability(player, itemStack, event.getCaught() instanceof Item ? 3 : 5, true); + } + } + } + + private void onReelIn(PlayerFishEvent event) { + Player player = event.getPlayer(); + getFishHook(player).ifPresent(hook -> { + event.setCancelled(true); + Optional gamingPlayer = hook.getGamingPlayer(); + if (gamingPlayer.isPresent()) { + ((AbstractGamingPlayer) gamingPlayer.get()).internalRightClick(); + return; + } + hook.onReelIn(); + }); + } + +// private void onCaughtFish(PlayerFishEvent event) { +// Player player = event.getPlayer(); +// getFishHook(player).ifPresent(hook -> { +// Optional gamingPlayer = hook.getGamingPlayer(); +// if (gamingPlayer.isPresent()) { +// if (gamingPlayer.get().handleRightClick()) { +// event.setCancelled(true); +// } +// return; +// } +// event.setCancelled(true); +// hook.onReelIn(); +// }); +// } + + private void onCastRod(PlayerFishEvent event) { + FishHook hook = event.getHook(); + Player player = event.getPlayer(); + Context context = Context.player(player); + FishingGears gears = new FishingGears(context); + if (!RequirementManager.isSatisfied(context, ConfigManager.mechanicRequirements())) { + this.destroy(player.getUniqueId()); + return; + } + if (!gears.canFish()) { + event.setCancelled(true); + return; + } + if (EventUtils.fireAndCheckCancel(new RodCastEvent(event, gears))) { + return; + } + plugin.debug(context); + CustomFishingHook customHook = new CustomFishingHook(plugin, hook, gears, context); + this.castHooks.put(player.getUniqueId(), customHook); + } + + private void onInGround(PlayerFishEvent event) { + final Player player = event.getPlayer(); + if (player.getGameMode() != GameMode.CREATIVE) { + ItemStack itemStack = player.getInventory().getItemInMainHand(); + if (itemStack.getType() != Material.FISHING_ROD) itemStack = player.getInventory().getItemInOffHand(); + if (itemStack.getType() == Material.FISHING_ROD) { + if (plugin.getItemManager().hasCustomDurability(itemStack)) { + event.setCancelled(true); + event.getHook().remove(); + plugin.getItemManager().decreaseDurability(player, itemStack, 2, true); + } + } + } + } + + @EventHandler + public void onHookStateChange(FishingHookStateEvent event) { + Player player = event.getPlayer(); + getFishHook(player).ifPresent(hook -> { + switch (event.getState()) { + case BITE -> hook.onBite(); + case LAND -> hook.onLand(); + case ESCAPE -> hook.onEscape(); + case LURE -> hook.onLure(); + } + }); + } + + @Override + public void destroy(UUID uuid) { + this.getFishHook(uuid).ifPresent(hook -> { + hook.destroy(); + this.castHooks.remove(uuid); + }); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/game/BukkitGameManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/game/BukkitGameManager.java new file mode 100644 index 00000000..16774b1a --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/game/BukkitGameManager.java @@ -0,0 +1,1057 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.game; + +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.fishing.CustomFishingHook; +import net.momirealms.customfishing.api.mechanic.game.*; +import net.momirealms.customfishing.api.mechanic.misc.value.TextValue; +import net.momirealms.customfishing.api.mechanic.requirement.ConditionalElement; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager; +import net.momirealms.customfishing.api.util.OffsetUtils; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import net.momirealms.customfishing.common.util.*; +import net.momirealms.sparrow.heart.SparrowHeart; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; + +@SuppressWarnings("DuplicatedCode") +public class BukkitGameManager implements GameManager { + + private final BukkitCustomFishingPlugin plugin; + private final Map gameFactoryMap = new HashMap<>(); + private final Map gameMap = new HashMap<>(); + private final LinkedHashMap, Double, Double>>>, Player>> gameConditions = new LinkedHashMap<>(); + private static final String EXPANSION_FOLDER = "expansions/minigame"; + + public BukkitGameManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + this.registerHoldGame(); + this.registerHoldV2Game(); + this.registerClickGame(); + this.registerTensionGame(); + this.registerDanceGame(); + this.registerAccurateClickGame(); + this.registerAccurateClickV2Game(); + this.registerAccurateClickV3Game(); + } + + @Override + public void load() { + this.loadExpansions(); + File file = new File(plugin.getDataFolder(), "game-conditions.yml"); + if (!file.exists()) { + plugin.getBoostrap().saveResource("game-conditions.yml", false); + } + YamlDocument lootConditionsConfig = plugin.getConfigManager().loadData(file); + for (Map.Entry entry : lootConditionsConfig.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section section) { + gameConditions.put(entry.getKey(), parseGameConditions(section)); + } + } + } + + @Override + public void unload() { + this.gameMap.clear(); + } + + private ConditionalElement, Double, Double>>>, Player> parseGameConditions(Section section) { + Section subSection = section.getSection("sub-groups"); + if (subSection == null) { + return new ConditionalElement<>( + plugin.getConfigManager().parseWeightOperation(section.getStringList("list")), + Map.of(), + plugin.getRequirementManager().parseRequirements(section.getSection("conditions"), false) + ); + } else { + HashMap, Double, Double>>>, Player>> subElements = new HashMap<>(); + for (Map.Entry entry : subSection.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + subElements.put(entry.getKey(), parseGameConditions(innerSection)); + } + } + return new ConditionalElement<>( + plugin.getConfigManager().parseWeightOperation(section.getStringList("list")), + subElements, + plugin.getRequirementManager().parseRequirements(section.getSection("conditions"), false) + ); + } + } + + @Override + public boolean registerGameType(String type, GameFactory gameFactory) { + if (gameFactoryMap.containsKey(type)) return false; + gameFactoryMap.put(type, gameFactory); + return true; + } + + @Override + public boolean unregisterGameType(String type) { + return gameFactoryMap.remove(type) != null; + } + + @Nullable + @Override + public GameFactory getGameFactory(String type) { + return gameFactoryMap.get(type); + } + + @Override + public Optional getGame(String id) { + return Optional.ofNullable(gameMap.get(id)); + } + + @Override + public boolean registerGame(Game game) { + if (gameMap.containsKey(game.id())) return false; + gameMap.put(game.id(), game); + return true; + } + + @Nullable + @Override + public Game getNextGame(Effect effect, Context context) { + HashMap lootWeightMap = new HashMap<>(); + for (ConditionalElement, Double, Double>>>, Player> conditionalElement : gameConditions.values()) { + modifyWeightMap(lootWeightMap, context, conditionalElement); + } + String gameID = WeightUtils.getRandom(lootWeightMap); + return Optional.ofNullable(gameID) + .map(id -> getGame(gameID).orElseThrow(() -> new RuntimeException("Could not find game " + gameID))) + .orElse(null); + } + + private void modifyWeightMap(Map weightMap, Context context, ConditionalElement, Double, Double>>>, Player> conditionalElement) { + if (conditionalElement == null) return; + if (RequirementManager.isSatisfied(context, conditionalElement.getRequirements())) { + for (Pair, Double, Double>> modifierPair : conditionalElement.getElement()) { + double previous = weightMap.getOrDefault(modifierPair.left(), 0d); + weightMap.put(modifierPair.left(), modifierPair.right().apply(context, previous)); + } + for (ConditionalElement, Double, Double>>>, Player> sub : conditionalElement.getSubElements().values()) { + modifyWeightMap(weightMap, context, sub); + } + } + } + + private GameBasics getGameBasics(Section section) { + GameBasics.Builder builder = GameBasics.builder(); + Object difficulty = section.get("difficulty", "20~80"); + if (difficulty instanceof String str) { + String[] split = str.split("~"); + builder.difficulty(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + } else if (difficulty instanceof Integer integer) { + builder.difficulty(integer); + } + Object time = section.get("time", 15); + if (time instanceof String str) { + String[] split = str.split("~"); + builder.time(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + } else if (time instanceof Integer integer) { + builder.time(integer); + } + return builder.build(); + } + + private void registerHoldGame() { + this.registerGameType("hold", (id, section) -> { + GameBasics basics = getGameBasics(section); + return new AbstractGame(id, basics) { + + private final int[] timeRequirements = section.getIntList("hold-time-requirements").stream().mapToInt(Integer::intValue).toArray(); + private final String judgementAreaImage = section.getString("subtitle.judgment-area"); + private final String pointerImage = section.getString("subtitle.pointer"); + private final int barEffectiveWidth = section.getInt("arguments.bar-effective-area-width"); + private final int judgementAreaOffset = section.getInt("arguments.judgment-area-offset"); + private final int judgementAreaWidth = section.getInt("arguments.judgment-area-width"); + private final int pointerIconWidth = section.getInt("arguments.pointer-icon-width"); + private final double punishment = section.getDouble("arguments.punishment"); + private final String[] progress = section.getStringList("progress").toArray(new String[0]); + private final double waterResistance = section.getDouble("arguments.water-resistance", 0.15); + private final double pullingStrength = section.getDouble("arguments.pulling-strength", 0.45); + private final double looseningLoss = section.getDouble("arguments.loosening-strength-loss", 0.3); + private final TextValue title = TextValue.auto(section.getString("title","{progress}")); + private final String font = section.getString("subtitle.font"); + private final String barImage = section.getString("subtitle.bar"); + private final String tip = section.getString("tip"); + + @Override + public BiFunction gamingPlayerProvider() { + return (customFishingHook, gameSetting) -> new AbstractGamingPlayer(customFishingHook, gameSetting) { + + private double hold_time; + private double judgement_position; + private double fish_position; + private double judgement_velocity; + private double fish_velocity; + private int timer; + private final int time_requirement = timeRequirements[ThreadLocalRandom.current().nextInt(timeRequirements.length)] * 1000; + private boolean played; + + @Override + public void arrangeTask() { + this.judgement_position = (double) (barEffectiveWidth - judgementAreaWidth) / 2; + this.task = plugin.getScheduler().asyncRepeating(this, 50, 33, TimeUnit.MILLISECONDS); + } + + @Override + public void tick() { + if (getPlayer().isSneaking()) addV(); + else reduceV(); + if (timer < 40 - (settings.difficulty() / 10)) { + timer++; + } else { + timer = 0; + if (Math.random() > ((double) 25 / (settings.difficulty() + 100))) { + burst(); + } + } + judgement_position += judgement_velocity; + fish_position += fish_velocity; + fraction(); + calibrate(); + if (fish_position >= judgement_position && fish_position + pointerIconWidth <= judgement_position + judgementAreaWidth) { + hold_time += 33; + } else { + hold_time -= punishment * 33; + } + if (hold_time >= time_requirement) { + setGameResult(true); + endGame(); + return; + } + hold_time = Math.max(0, Math.min(hold_time, time_requirement)); + showUI(); + } + + private void burst() { + if (Math.random() < (judgement_position / barEffectiveWidth)) { + judgement_velocity = -1 - 0.8 * Math.random() * ((double) settings.difficulty() / 15); + } else { + judgement_velocity = 1 + 0.8 * Math.random() * ((double) settings.difficulty() / 15); + } + } + + private void fraction() { + if (judgement_velocity > 0) { + judgement_velocity -= waterResistance; + if (judgement_velocity < 0) judgement_velocity = 0; + } else { + judgement_velocity += waterResistance; + if (judgement_velocity > 0) judgement_velocity = 0; + } + } + + private void reduceV() { + fish_velocity -= looseningLoss; + } + + private void addV() { + played = true; + fish_velocity += pullingStrength; + } + + private void calibrate() { + if (fish_position < 0) { + fish_position = 0; + fish_velocity = 0; + } + if (fish_position + pointerIconWidth > barEffectiveWidth) { + fish_position = barEffectiveWidth - pointerIconWidth; + fish_velocity = 0; + } + if (judgement_position < 0) { + judgement_position = 0; + judgement_velocity = 0; + } + if (judgement_position + judgementAreaWidth > barEffectiveWidth) { + judgement_position = barEffectiveWidth - judgementAreaWidth; + judgement_velocity = 0; + } + } + + private void showUI() { + String bar = AdventureHelper.surroundWithMiniMessageFont(barImage, font) + + OffsetUtils.getOffsetChars((int) (judgementAreaOffset + judgement_position)) + + AdventureHelper.surroundWithMiniMessageFont(judgementAreaImage, font) + + OffsetUtils.getOffsetChars((int) (barEffectiveWidth - judgement_position - judgementAreaWidth)) + + OffsetUtils.getOffsetChars((int) (-barEffectiveWidth - 1 + fish_position)) + + AdventureHelper.surroundWithMiniMessageFont(pointerImage, font) + + OffsetUtils.getOffsetChars((int) (barEffectiveWidth - fish_position - pointerIconWidth + 1)); + customFishingHook.getContext().arg(ContextKeys.PROGRESS, progress[(int) ((hold_time / time_requirement) * progress.length)]); + SparrowHeart.getInstance().sendTitle(super.getPlayer(), AdventureHelper.miniMessageToJson(tip != null && !played ? tip : title.render(customFishingHook.getContext())), AdventureHelper.jsonToMiniMessage(bar), 0, 20, 0); + } + }; + } + }; + }); + } + + private void registerHoldV2Game() { + this.registerGameType("hold_v2", (id, section) -> { + GameBasics basics = getGameBasics(section); + return new AbstractGame(id, basics) { + + private final int[] timeRequirements = section.getIntList("hold-time-requirements").stream().mapToInt(Integer::intValue).toArray(); + private final String judgementAreaImage = section.getString("subtitle.judgment-area"); + private final String pointerImage = section.getString("subtitle.pointer"); + private final int barEffectiveWidth = section.getInt("arguments.bar-effective-area-width"); + private final int judgementAreaOffset = section.getInt("arguments.judgment-area-offset"); + private final int judgementAreaWidth = section.getInt("arguments.judgment-area-width"); + private final int pointerIconWidth = section.getInt("arguments.pointer-icon-width"); + private final double punishment = section.getDouble("arguments.punishment"); + private final String[] progress = section.getStringList("progress").toArray(new String[0]); + private final double waterResistance = section.getDouble("arguments.water-resistance", 0.15); + private final double pullingStrength = section.getDouble("arguments.pulling-strength", 3d); + private final double looseningLoss = section.getDouble("arguments.loosening-strength-loss", 0.5); + private final TextValue title = TextValue.auto(section.getString("title", "{progress}")); + private final String font = section.getString("subtitle.font"); + private final String barImage = section.getString("subtitle.bar"); + private final String tip = section.getString("tip"); + private final boolean left = section.getBoolean("left-click", false); + + @Override + public BiFunction gamingPlayerProvider() { + return (customFishingHook, gameSetting) -> new AbstractGamingPlayer(customFishingHook, gameSetting) { + private double hold_time; + private double judgement_position; + private double fish_position; + private double judgement_velocity; + private double fish_velocity; + private int timer; + private final int time_requirement = timeRequirements[ThreadLocalRandom.current().nextInt(timeRequirements.length)] * 1000; + private boolean played; + + @Override + public void arrangeTask() { + this.judgement_position = (double) (barEffectiveWidth - judgementAreaWidth) / 2; + this.task = plugin.getScheduler().asyncRepeating(this, 50, 33, TimeUnit.MILLISECONDS); + } + + @Override + public void tick() { + if (timer < 40 - (settings.difficulty() / 10)) { + timer++; + } else { + timer = 0; + if (Math.random() > ((double) 25 / (settings.difficulty() + 100))) { + burst(); + } + } + judgement_position += judgement_velocity; + fish_position += fish_velocity; + fraction(); + calibrate(); + if (fish_position >= judgement_position && fish_position + pointerIconWidth <= judgement_position + judgementAreaWidth) { + hold_time += 33; + } else { + hold_time -= punishment * 33; + } + if (hold_time >= time_requirement) { + setGameResult(true); + endGame(); + return; + } + hold_time = Math.max(0, Math.min(hold_time, time_requirement)); + showUI(); + } + + private void burst() { + if (Math.random() < (judgement_position / barEffectiveWidth)) { + judgement_velocity = -1 - 0.8 * Math.random() * ((double) settings.difficulty() / 15); + } else { + judgement_velocity = 1 + 0.8 * Math.random() * ((double) settings.difficulty() / 15); + } + } + + private void fraction() { + if (judgement_velocity > 0) { + judgement_velocity -= waterResistance; + if (judgement_velocity < 0) judgement_velocity = 0; + } else { + judgement_velocity += waterResistance; + if (judgement_velocity > 0) judgement_velocity = 0; + } + fish_velocity -= looseningLoss; + if (fish_velocity < -10 * looseningLoss) { + fish_velocity = -10 * looseningLoss; + } + } + + private void calibrate() { + if (fish_position < 0) { + fish_position = 0; + fish_velocity = 0; + } + if (fish_position + pointerIconWidth > barEffectiveWidth) { + fish_position = barEffectiveWidth - pointerIconWidth; + fish_velocity = 0; + } + if (judgement_position < 0) { + judgement_position = 0; + judgement_velocity = 0; + } + if (judgement_position + judgementAreaWidth > barEffectiveWidth) { + judgement_position = barEffectiveWidth - judgementAreaWidth; + judgement_velocity = 0; + } + } + + @Override + public void handleRightClick() { + if (left) { + setGameResult(false); + endGame(); + return; + } + played = true; + fish_velocity = pullingStrength; + return; + } + + @Override + public boolean handleLeftClick() { + if (left) { + played = true; + fish_velocity = pullingStrength; + } + return false; + } + + private void showUI() { + String bar = AdventureHelper.surroundWithMiniMessageFont(barImage, font) + + OffsetUtils.getOffsetChars((int) (judgementAreaOffset + judgement_position)) + + AdventureHelper.surroundWithMiniMessageFont(judgementAreaImage, font) + + OffsetUtils.getOffsetChars((int) (barEffectiveWidth - judgement_position - judgementAreaWidth)) + + OffsetUtils.getOffsetChars((int) (-barEffectiveWidth - 1 + fish_position)) + + AdventureHelper.surroundWithMiniMessageFont(pointerImage, font) + + OffsetUtils.getOffsetChars((int) (barEffectiveWidth - fish_position - pointerIconWidth + 1)); + hook.getContext().arg(ContextKeys.PROGRESS, progress[(int) ((hold_time / time_requirement) * progress.length)]); + SparrowHeart.getInstance().sendTitle(getPlayer(), AdventureHelper.miniMessageToJson(tip != null && !played ? tip : title.render(hook.getContext())), AdventureHelper.miniMessageToJson(bar), 0, 20, 0); + } + }; + } + }; + }); + } + + private void registerClickGame() { + this.registerGameType("click", ((id, section) -> { + GameBasics basics = getGameBasics(section); + return new AbstractGame(id, basics) { + + private final TextValue title = TextValue.auto(section.getString("title","{progress}")); + private final TextValue subtitle = TextValue.auto(section.getString("subtitle", "Click {clicks} times to win. Time left {time_left}s")); + + @Override + public BiFunction gamingPlayerProvider() { + return (customFishingHook, gameSetting) -> new AbstractGamingPlayer(customFishingHook, gameSetting) { + private int clickedTimes; + private final int requiredTimes = settings.difficulty(); + + @Override + public void arrangeTask() { + hook.getContext().arg(ContextKeys.REQUIRED_TIMES, requiredTimes); + super.arrangeTask(); + } + + @Override + protected void tick() { + showUI(); + } + + @Override + public void handleRightClick() { + handleClicks(); + } + + @Override + public boolean internalLeftClick() { + handleClicks(); + return true; + } + + private void handleClicks() { + clickedTimes++; + if (clickedTimes >= requiredTimes) { + showUI(); + setGameResult(true); + endGame(); + } + } + + private void showUI() { + hook.getContext().arg(ContextKeys.CLICKS_LEFT, requiredTimes - clickedTimes); + hook.getContext().arg(ContextKeys.PROGRESS, String.valueOf(clickedTimes)); + SparrowHeart.getInstance().sendTitle(getPlayer(), AdventureHelper.miniMessageToJson(title.render(hook.getContext())), AdventureHelper.miniMessageToJson(subtitle.render(hook.getContext())), 0, 20, 0); + } + }; + } + }; + })); + } + + private void registerTensionGame() { + this.registerGameType("tension", ((id, section) -> { + GameBasics basics = getGameBasics(section); + return new AbstractGame(id, basics) { + + private final int fishIconWidth = section.getInt("arguments.fish-icon-width"); + private final String fishImage = section.getString("subtitle.fish"); + private final String[] tension = section.getStringList("tension").toArray(new String[0]); + private final String[] strugglingFishImage = section.getStringList("subtitle.struggling-fish").toArray(new String[0]); + private final int barEffectiveWidth = section.getInt("arguments.bar-effective-area-width"); + private final int fishOffset = section.getInt("arguments.fish-offset"); + private final int fishStartPosition = section.getInt("arguments.fish-start-position"); + private final int successPosition = section.getInt("arguments.success-position"); + private final double ultimateTension = section.getDouble("arguments.ultimate-tension", 50d); + private final double normalIncrease = section.getDouble("arguments.normal-pull-tension-increase", 1d); + private final double strugglingIncrease = section.getDouble("arguments.struggling-tension-increase", 2d); + private final double tensionLoss = section.getDouble("arguments.loosening-tension-loss", 2d); + private final TextValue title = TextValue.auto(section.getString("title","{progress}")); + private final String font = section.getString("subtitle.font"); + private final String barImage = section.getString("subtitle.bar"); + private final String tip = section.getString("tip"); + + @Override + public BiFunction gamingPlayerProvider() { + return (customFishingHook, gameSetting) -> new AbstractGamingPlayer(customFishingHook, gameSetting) { + + private int fish_position = fishStartPosition; + private double strain; + private int struggling_time; + private boolean played; + + @Override + public void arrangeTask() { + this.task = plugin.getScheduler().asyncRepeating(this, 50, 40, TimeUnit.MILLISECONDS); + } + + @Override + protected void tick() { + if (struggling_time <= 0) { + if (Math.random() < ((double) settings.difficulty() / 4000)) { + struggling_time = (int) (10 + Math.random() * (settings.difficulty() / 4)); + } + } else { + struggling_time--; + } + if (getPlayer().isSneaking()) pull(); + else loosen(); + if (fish_position < successPosition - fishIconWidth - 1) { + setGameResult(true); + endGame(); + return; + } + if (fish_position + fishIconWidth > barEffectiveWidth || strain >= ultimateTension) { + setGameResult(false); + endGame(); + return; + } + showUI(); + } + + private void pull() { + played = true; + if (struggling_time > 0) { + strain += (strugglingIncrease + ((double) settings.difficulty() / 50)); + fish_position -= 1; + } else { + strain += normalIncrease; + fish_position -= 2; + } + } + + private void loosen() { + fish_position++; + strain -= tensionLoss; + } + + private void showUI() { + String bar = AdventureHelper.surroundWithMiniMessageFont(barImage, font) + + OffsetUtils.getOffsetChars(fishOffset + fish_position) + + AdventureHelper.surroundWithMiniMessageFont((struggling_time > 0 ? strugglingFishImage[struggling_time % strugglingFishImage.length] : fishImage), font) + + OffsetUtils.getOffsetChars(barEffectiveWidth - fish_position - fishIconWidth); + strain = Math.max(0, Math.min(strain, ultimateTension)); + hook.getContext().arg(ContextKeys.PROGRESS, tension[(int) ((strain / ultimateTension) * tension.length)]); + SparrowHeart.getInstance().sendTitle(getPlayer(), AdventureHelper.miniMessageToJson(tip != null && !played ? tip : title.render(hook.getContext())), AdventureHelper.miniMessageToJson(bar), 0, 20, 0); + } + }; + } + }; + })); + } + + private void registerDanceGame() { + this.registerGameType("dance", ((id, section) -> { + GameBasics basics = getGameBasics(section); + return new AbstractGame(id, basics) { + private final TextValue subtitle = TextValue.auto(section.getString("subtitle", "Dance to win. Time left: {time_left}s")); + private final String leftNot = section.getString("title.left-button"); + private final String leftCorrect = section.getString("title.left-button-correct"); + private final String leftWrong = section.getString("title.left-button-wrong"); + private final String leftCurrent = section.getString("title.left-button-current"); + private final String rightNot = section.getString("title.right-button"); + private final String rightCorrect = section.getString("title.right-button-correct"); + private final String rightWrong = section.getString("title.right-button-wrong"); + private final String rightCurrent = section.getString("title.right-button-current"); + private final String upNot = section.getString("title.up-button"); + private final String upCorrect = section.getString("title.up-button-correct"); + private final String upWrong = section.getString("title.up-button-wrong"); + private final String upCurrent = section.getString("title.up-button-current"); + private final String downNot = section.getString("title.down-button"); + private final String downCorrect = section.getString("title.down-button-correct"); + private final String downWrong = section.getString("title.down-button-wrong"); + private final String downCurrent = section.getString("title.down-button-current"); + private final int maxShown = section.getInt("title.display-amount", 7); + private final String tip = section.getString("tip"); + private final boolean easy = section.getBoolean("easy", false); + private final String correctSound = section.getString("sound.correct", "minecraft:block.amethyst_block.hit"); + private final String wrongSound = section.getString("sound.wrong", "minecraft:block.anvil.land"); + + @Override + public BiFunction gamingPlayerProvider() { + return (customFishingHook, gameSetting) -> new AbstractGamingPlayer(customFishingHook, gameSetting) { + + private int clickedTimes; + private int requiredTimes; + // 0 = left / 1 = right / 2 = up / 3 = down + private int[] order; + boolean fail = false; + + @Override + public void arrangeTask() { + requiredTimes = settings.difficulty() / 4; + order = new int[requiredTimes]; + for (int i = 0; i < requiredTimes; i++) { + order[i] = ThreadLocalRandom.current().nextInt(0, easy ? 2 : 4); + } + this.task = plugin.getScheduler().asyncRepeating(this, 50, 50, TimeUnit.MILLISECONDS); + } + + @Override + protected void tick() { + showUI(); + if (tip != null) { + SparrowHeart.getInstance().sendActionBar(getPlayer(), AdventureHelper.miniMessageToJson(tip)); + } + } + + @Override + public boolean handleLeftClick() { + if (order[clickedTimes] != 0) { + handleWrongAction(); + return true; + } + handleCorrectAction(); + return true; + } + + @Override + public void handleRightClick() { + if (order[clickedTimes] != 1) { + handleWrongAction(); + return; + } + handleCorrectAction(); + } + + @Override + public boolean handleJump() { + if (order[clickedTimes] != 2) { + handleWrongAction(); + return false; + } + handleCorrectAction(); + return false; + } + + @Override + public boolean handleSneak() { + if (order[clickedTimes] != 3) { + handleWrongAction(); + return false; + } + handleCorrectAction(); + return false; + } + + private void handleCorrectAction() { + plugin.getSenderFactory().getAudience(getPlayer()).playSound(Sound.sound(Key.key(correctSound), Sound.Source.PLAYER, 1,1)); + clickedTimes++; + if (clickedTimes >= requiredTimes) { + setGameResult(true); + showUI(); + endGame(); + } + } + + private void handleWrongAction() { + setGameResult(false); + fail = true; + showUI(); + plugin.getSenderFactory().getAudience(getPlayer()).playSound(Sound.sound(Key.key(wrongSound), Sound.Source.PLAYER, 1,1)); + endGame(); + } + + private void showUI() { + try { + if (requiredTimes <= maxShown) { + StringBuilder sb = new StringBuilder(); + for (int x = 0; x < requiredTimes; x++) { + if (x < clickedTimes) { + switch (order[x]) { + case 0 -> sb.append(leftCorrect); + case 1 -> sb.append(rightCorrect); + case 2 -> sb.append(upCorrect); + case 3 -> sb.append(downCorrect); + } + } else if (clickedTimes == x) { + switch (order[x]) { + case 0 -> sb.append(fail ? leftWrong : leftCurrent); + case 1 -> sb.append(fail ? rightWrong : rightCurrent); + case 2 -> sb.append(fail ? upWrong : upCurrent); + case 3 -> sb.append(fail ? downWrong : downCurrent); + } + } else { + switch (order[x]) { + case 0 -> sb.append(leftNot); + case 1 -> sb.append(rightNot); + case 2 -> sb.append(upNot); + case 3 -> sb.append(downNot); + } + } + } + SparrowHeart.getInstance().sendTitle(getPlayer(), AdventureHelper.miniMessageToJson(sb.toString()), AdventureHelper.miniMessageToJson(subtitle.render(hook.getContext())), 0, 20, 0); + } else { + int half = (maxShown - 1) / 2; + int low = clickedTimes - half; + int high = clickedTimes + half; + if (low < 0) { + high += (-low); + low = 0; + } else if (high >= requiredTimes) { + low -= (high - requiredTimes + 1); + high = requiredTimes - 1; + } + StringBuilder sb = new StringBuilder(); + for (int x = low; x < high + 1; x++) { + if (x < clickedTimes) { + switch (order[x]) { + case 0 -> sb.append(leftCorrect); + case 1 -> sb.append(rightCorrect); + case 2 -> sb.append(upCorrect); + case 3 -> sb.append(downCorrect); + } + } else if (clickedTimes == x) { + switch (order[x]) { + case 0 -> sb.append(fail ? leftWrong : leftCurrent); + case 1 -> sb.append(fail ? rightWrong : rightCurrent); + case 2 -> sb.append(fail ? upWrong : upCurrent); + case 3 -> sb.append(fail ? downWrong : downCurrent); + } + } else { + switch (order[x]) { + case 0 -> sb.append(leftNot); + case 1 -> sb.append(rightNot); + case 2 -> sb.append(upNot); + case 3 -> sb.append(downNot); + } + } + } + SparrowHeart.getInstance().sendTitle(getPlayer(), AdventureHelper.miniMessageToJson(sb.toString()), subtitle.render(hook.getContext()), 0, 20, 0); + } + } catch (Exception e) { + plugin.getPluginLogger().warn("Failed to show `dance` UI", e); + } + } + }; + } + }; + })); + } + + private void registerAccurateClickGame() { + this.registerGameType("accurate_click", ((id, section) -> { + GameBasics basics = getGameBasics(section); + + Set chances = Objects.requireNonNull(section.getSection("success-rate-sections")).getRoutesAsStrings(false); + double[] successRate = new double[chances.size()]; + for(int i = 0; i < chances.size(); i++) + successRate[i] = section.getDouble("success-rate-sections." + (i + 1)); + + return new AbstractGame(id, basics) { + + private final int widthPerSection = section.getInt("arguments.width-per-section", 16); + private final int totalWidth = chances.size() * widthPerSection - 1; + private final int pointerOffset = section.getInt("arguments.pointer-offset"); + private final int pointerWidth = section.getInt("arguments.pointer-width"); + private final List title = ListUtils.toList(section.get("title")); + private final String font = section.getString("subtitle.font"); + private final String barImage = section.getString("subtitle.bar"); + private final String pointerImage = section.getString("subtitle.pointer"); + + @Override + public BiFunction gamingPlayerProvider() { + return (customFishingHook, gameSetting) -> new AbstractGamingPlayer(customFishingHook, gameSetting) { + + private int progress = -1; + private boolean face = true; + private final TextValue sendTitle = TextValue.auto(title.get(RandomUtils.generateRandomInt(0, title.size() - 1))); + + @Override + public void arrangeTask() { + var period = Math.min(200, ((double) 10*(200-settings.difficulty()))/((double) (1+4*settings.difficulty()))); + this.task = plugin.getScheduler().asyncRepeating(this, 10, (long) 10, TimeUnit.MILLISECONDS); + } + + @Override + protected void tick() { + if (face) progress++; + else progress--; + if (progress > totalWidth) { + face = !face; + progress = 2 * totalWidth - progress; + } else if (progress < 0) { + face = !face; + progress = -progress; + } + if (!isValid()) return; + showUI(); + } + + @Override + public boolean isSuccessful() { + if (isTimeOut) return false; + int last = progress / widthPerSection; + return (Math.random() < successRate[last]); + } + + private void showUI() { + String bar = AdventureHelper.surroundWithMiniMessageFont(barImage, font) + + OffsetUtils.getOffsetChars(pointerOffset + progress) + + AdventureHelper.surroundWithMiniMessageFont(pointerImage, font) + + OffsetUtils.getOffsetChars(totalWidth - progress - pointerWidth); + SparrowHeart.getInstance().sendTitle(getPlayer(), AdventureHelper.miniMessageToJson(sendTitle.render(hook.getContext())), AdventureHelper.miniMessageToJson(bar), 0, 10, 0); + } + }; + } + }; + })); + } + + private void registerAccurateClickV2Game() { + this.registerGameType("accurate_click_v2", ((id, section) -> { + GameBasics basics = getGameBasics(section); + return new AbstractGame(id, basics) { + + private final String barWidth = section.getString("title.total-width", "15~20"); + private final String barSuccess = section.getString("title.success-width","3~4"); + private final String barBody = section.getString("title.body",""); + private final String left = section.getString("title.left",""); + private final String right = section.getString("title.right",""); + private final String barPointer = section.getString("title.pointer", ""); + private final String barTarget = section.getString("title.target",""); + private final String subtitle = section.getString("subtitle", "Reel in at the most critical moment"); + + @Override + public BiFunction gamingPlayerProvider() { + + int minWidth = Integer.parseInt(barWidth.split("~")[0]); + int maxWidth = Integer.parseInt(barWidth.split("~")[1]); + int minSuccess = Integer.parseInt(barSuccess.split("~")[0]); + int maxSuccess = Integer.parseInt(barSuccess.split("~")[1]); + + return (customFishingHook, gameSetting) -> new AbstractGamingPlayer(customFishingHook, gameSetting) { + + private final int totalWidth = RandomUtils.generateRandomInt(minWidth, maxWidth); + private final int successWidth = RandomUtils.generateRandomInt(minSuccess, maxSuccess); + private final int successPosition = ThreadLocalRandom.current().nextInt((totalWidth - successWidth + 1)) + 1; + private int currentIndex = 0; + private int timer = 0; + private boolean face = true; + + @Override + protected void tick() { + timer++; + if (timer % (21 - settings.difficulty() / 5) == 0) { + movePointer(); + } + showUI(); + } + + private void movePointer() { + if (face) { + currentIndex++; + if (currentIndex >= totalWidth - 1) { + face = false; + } + } else { + currentIndex--; + if (currentIndex <= 0) { + face = true; + } + } + } + + private void showUI() { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 1; i <= totalWidth; i++) { + if (i == currentIndex + 1) { + stringBuilder.append(barPointer); + continue; + } + if (i >= successPosition && i <= successPosition + successWidth - 1) { + stringBuilder.append(barTarget); + continue; + } + stringBuilder.append(barBody); + } + + SparrowHeart.getInstance().sendTitle(getPlayer(), AdventureHelper.miniMessageToJson(left + stringBuilder + right), AdventureHelper.miniMessageToJson(subtitle), 0, 20, 0); + } + + @Override + public boolean isSuccessful() { + if (isTimeOut) return false; + return currentIndex + 1 <= successPosition + successWidth - 1 && currentIndex + 1 >= successPosition; + } + }; + } + }; + })); + } + + private void registerAccurateClickV3Game() { + this.registerGameType("accurate_click_v3", ((id, section) -> { + GameBasics basics = getGameBasics(section); + return new AbstractGame(id, basics) { + + private final String font = section.getString("subtitle.font"); + private final String pointerImage = section.getString("subtitle.pointer"); + private final String barImage = section.getString("subtitle.bar"); + private final String judgementAreaImage = section.getString("subtitle.judgment-area"); + private final List titles = ListUtils.toList(section.get("title")); + private final int barEffectiveWidth = section.getInt("arguments.bar-effective-area-width"); + private final int judgementAreaWidth = section.getInt("arguments.judgment-area-width"); + private final int judgementAreaOffset = section.getInt("arguments.judgment-area-offset"); + private final int pointerIconWidth = section.getInt("arguments.pointer-icon-width"); + private final int pointerOffset = section.getInt("arguments.pointer-offset"); + + @Override + public BiFunction gamingPlayerProvider() { + return (customFishingHook, gameSetting) -> new AbstractGamingPlayer(customFishingHook, gameSetting) { + + private int progress = -1; + private boolean face = true; + private final int judgement_position = RandomUtils.generateRandomInt(0, barEffectiveWidth - judgementAreaWidth); + private final TextValue title = TextValue.auto(titles.get(RandomUtils.generateRandomInt(0, titles.size() - 1))); + + @Override + public void arrangeTask() { + var period = ((double) 10*(200-settings.difficulty()))/((double) (1+4*settings.difficulty())); + this.task = plugin.getScheduler().asyncRepeating(this, 50, (long) period, TimeUnit.MILLISECONDS); + } + + @Override + protected void tick() { + if (face) { + progress++; + if (progress >= barEffectiveWidth - 1) { + face = false; + } + } else { + progress--; + if (progress <= 0) { + face = true; + } + } + showUI(); + } + + private void showUI() { + String bar = AdventureHelper.surroundWithMiniMessageFont(barImage, font) + + OffsetUtils.getOffsetChars(judgementAreaOffset + judgement_position) + + AdventureHelper.surroundWithMiniMessageFont(judgementAreaImage, font) + + OffsetUtils.getOffsetChars(barEffectiveWidth - judgement_position - judgementAreaWidth) + + OffsetUtils.getOffsetChars(progress + pointerOffset) + + AdventureHelper.surroundWithMiniMessageFont(pointerImage, font) + + OffsetUtils.getOffsetChars(barEffectiveWidth - progress - pointerIconWidth + 1); + SparrowHeart.getInstance().sendTitle(getPlayer(), AdventureHelper.miniMessageToJson(title.render(hook.getContext())), AdventureHelper.miniMessageToJson(bar), 0, 20, 0); + } + + @Override + public boolean isSuccessful() { + if (isTimeOut) return false; + return progress < judgement_position + judgementAreaWidth && progress >= judgement_position; + } + }; + } + }; + })); + } + + /** + * Loads minigame expansions from the expansion folder. + */ + @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, GameExpansion.class); + classes.add(expansionClass); + } catch (IOException | ClassNotFoundException e) { + plugin.getPluginLogger().warn("Failed to load expansion: " + expansionJar.getName(), e); + } + } + } + try { + for (Class expansionClass : classes) { + GameExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); + unregisterGameType(expansion.getGameType()); + registerGameType(expansion.getGameType(), expansion.getGameFactory()); + plugin.getPluginLogger().info("Loaded minigame expansion: " + expansion.getGameType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor() ); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + plugin.getPluginLogger().warn("Error occurred when creating expansion instance.", e); + } + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/hook/BukkitHookManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/hook/BukkitHookManager.java new file mode 100644 index 00000000..f883ada4 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/hook/BukkitHookManager.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.hook; + +import com.saicone.rtag.item.ItemTagStream; +import net.jpountz.lz4.LZ4Compressor; +import net.jpountz.lz4.LZ4Factory; +import net.jpountz.lz4.LZ4FastDecompressor; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ScoreComponent; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; +import net.momirealms.customfishing.api.mechanic.hook.HookConfig; +import net.momirealms.customfishing.api.mechanic.hook.HookManager; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager; +import net.momirealms.customfishing.bukkit.item.damage.CustomDurabilityItem; +import net.momirealms.customfishing.bukkit.item.damage.DurabilityItem; +import net.momirealms.customfishing.bukkit.item.damage.VanillaDurabilityItem; +import net.momirealms.customfishing.bukkit.util.PlayerUtils; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import net.momirealms.customfishing.common.item.Item; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.io.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; + +public class BukkitHookManager implements HookManager, Listener { + + private final BukkitCustomFishingPlugin plugin; + private final HashMap hooks = new HashMap<>(); + private LZ4Factory factory; + + public BukkitHookManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + this.factory = LZ4Factory.fastestInstance(); + } + + @Override + public void unload() { + HandlerList.unregisterAll(this); + } + + @Override + public void load() { + Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap()); + plugin.debug("Loaded " + hooks.size() + " hooks"); + } + + @Override + public boolean registerHook(HookConfig hook) { + if (hooks.containsKey(hook.id())) return false; + hooks.put(hook.id(), hook); + return true; + } + + @NotNull + @Override + public Optional getHook(String id) { + return Optional.ofNullable(hooks.get(id)); + } + + @Override + public Optional getHookID(ItemStack rod) { + if (rod == null || rod.getType() != Material.FISHING_ROD || rod.getAmount() == 0) + return Optional.empty(); + + Item wrapped = plugin.getItemManager().wrap(rod); + return wrapped.getTag("CustomFishing", "hook_id").map(o -> (String) o); + } + + @EventHandler (ignoreCancelled = true) + public void onDragDrop(InventoryClickEvent event) { + final Player player = (Player) event.getWhoClicked(); + if (event.getClickedInventory() != player.getInventory()) + return; + if (player.getGameMode() != GameMode.SURVIVAL) + return; + ItemStack clicked = event.getCurrentItem(); + if (clicked == null || clicked.getType() != Material.FISHING_ROD) + return; + if (plugin.getFishingManager().getFishHook(player).isPresent()) + return; + ItemStack cursor = event.getCursor(); + if (cursor.getType() == Material.AIR) { + if (event.getClick() != ClickType.RIGHT) { + return; + } + Item wrapped = plugin.getItemManager().wrap(clicked); + if (!wrapped.hasTag("CustomFishing", "hook_id")) { + return; + } + event.setCancelled(true); + String id = (String) wrapped.getTag("CustomFishing", "hook_id").orElseThrow(); + byte[] hookItemBase64 = (byte[]) wrapped.getTag("CustomFishing", "hook_stack").orElse(null); + int damage = (int) wrapped.getTag("CustomFishing", "hook_damage").orElse(0); + ItemStack itemStack; + if (hookItemBase64 != null) { + itemStack = bytesToHook(hookItemBase64); + } else { + itemStack = plugin.getItemManager().buildInternal(Context.player(player), id); + } + plugin.getItemManager().setDurability(player, itemStack, damage); + + wrapped.removeTag("CustomFishing", "hook_id"); + wrapped.removeTag("CustomFishing", "hook_stack"); + wrapped.removeTag("CustomFishing", "hook_damage"); + wrapped.removeTag("CustomFishing", "hook_max_damage"); + + event.setCursor(itemStack); + + List previousLore = wrapped.lore().orElse(new ArrayList<>()); + List newLore = new ArrayList<>(); + for (String previous : previousLore) { + Component component = AdventureHelper.jsonToComponent(previous); + if (component instanceof ScoreComponent scoreComponent && scoreComponent.name().equals("cf") && scoreComponent.objective().equals("hook")) { + continue; + } + newLore.add(previous); + } + wrapped.lore(newLore); + + wrapped.load(); + return; + } + + String hookID = plugin.getItemManager().getItemID(cursor); + Optional setting = getHook(hookID); + if (setting.isEmpty()) { + return; + } + + Context context = Context.player(player); + HookConfig hookConfig = setting.get(); + Optional modifier = plugin.getEffectManager().getEffectModifier(hookID, MechanicType.HOOK); + if (modifier.isPresent()) { + if (!RequirementManager.isSatisfied(context, modifier.get().requirements())) { + return; + } + } + event.setCancelled(true); + + ItemStack clonedHook = cursor.clone(); + clonedHook.setAmount(1); + cursor.setAmount(cursor.getAmount() - 1); + + Item wrapped = plugin.getItemManager().wrap(clicked); + String previousHookID = (String) wrapped.getTag("CustomFishing", "hook_id").orElse(null); + if (previousHookID != null) { + int previousHookDamage = (int) wrapped.getTag("CustomFishing", "hook_damage").orElse(0); + ItemStack previousItemStack; + byte[] stackBytes = (byte[]) wrapped.getTag("CustomFishing", "hook_stack").orElse(null); + if (stackBytes != null) { + previousItemStack = bytesToHook(stackBytes); + } else { + previousItemStack = plugin.getItemManager().buildInternal(Context.player(player), previousHookID); + } + if (previousItemStack != null) { + plugin.getItemManager().setDurability(player, previousItemStack, previousHookDamage); + if (cursor.getAmount() == 0) { + event.setCursor(previousItemStack); + } else { + PlayerUtils.giveItem(player, previousItemStack, 1); + } + } + } + + Item wrappedHook = plugin.getItemManager().wrap(clonedHook); + DurabilityItem durabilityItem; + if (wrappedHook.hasTag("CustomFishing", "max_dur")) { + durabilityItem = new CustomDurabilityItem(wrappedHook); + } else { + durabilityItem = new VanillaDurabilityItem(wrappedHook); + } + + wrapped.setTag(hookID, "CustomFishing", "hook_id"); + wrapped.setTag(hookToBytes(clonedHook), "CustomFishing", "hook_stack"); + wrapped.setTag(durabilityItem.damage(), "CustomFishing", "hook_damage"); + wrapped.setTag(durabilityItem.maxDamage(), "CustomFishing", "hook_max_damage"); + + List previousLore = wrapped.lore().orElse(new ArrayList<>()); + List newLore = new ArrayList<>(); + List durabilityLore = new ArrayList<>(); + for (String previous : previousLore) { + Component component = AdventureHelper.jsonToComponent(previous); + if (component instanceof ScoreComponent scoreComponent && scoreComponent.name().equals("cf")) { + if (scoreComponent.objective().equals("hook")) { + continue; + } else if (scoreComponent.objective().equals("durability")) { + durabilityLore.add(previous); + continue; + } + } + newLore.add(previous); + } + for (String lore : hookConfig.lore()) { + ScoreComponent.Builder builder = Component.score().name("cf").objective("hook"); + builder.append(AdventureHelper.miniMessage(lore.replace("{dur}", String.valueOf(durabilityItem.maxDamage() - durabilityItem.damage())).replace("{max}", String.valueOf(durabilityItem.maxDamage())))); + newLore.add(AdventureHelper.componentToJson(builder.build())); + } + newLore.addAll(durabilityLore); + wrapped.lore(newLore); + wrapped.load(); + } + + private byte[] hookToBytes(ItemStack hook) { + try { + byte[] data = ItemTagStream.INSTANCE.toBytes(hook); + int decompressedLength = data.length; + LZ4Compressor compressor = factory.fastCompressor(); + int maxCompressedLength = compressor.maxCompressedLength(decompressedLength); + byte[] compressed = new byte[maxCompressedLength]; + int compressedLength = compressor.compress(data, 0, decompressedLength, compressed, 0, maxCompressedLength); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + DataOutputStream outputStream = new DataOutputStream(byteArrayOutputStream); + outputStream.writeInt(decompressedLength); + outputStream.write(compressed, 0, compressedLength); + outputStream.close(); + + return byteArrayOutputStream.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private ItemStack bytesToHook(byte[] bytes) { + try { + DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(bytes)); + int decompressedLength = inputStream.readInt(); + byte[] compressed = new byte[inputStream.available()]; + inputStream.readFully(compressed); + + LZ4FastDecompressor decompressor = factory.fastDecompressor(); + byte[] restored = new byte[decompressedLength]; + decompressor.decompress(compressed, 0, restored, 0, decompressedLength); + + return ItemTagStream.INSTANCE.fromBytes(restored); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/integration/BukkitIntegrationManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/integration/BukkitIntegrationManager.java new file mode 100644 index 00000000..71b9aae2 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/integration/BukkitIntegrationManager.java @@ -0,0 +1,254 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.integration; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.integration.*; +import net.momirealms.customfishing.bukkit.block.BukkitBlockManager; +import net.momirealms.customfishing.bukkit.entity.BukkitEntityManager; +import net.momirealms.customfishing.bukkit.integration.block.ItemsAdderBlockProvider; +import net.momirealms.customfishing.bukkit.integration.block.OraxenBlockProvider; +import net.momirealms.customfishing.bukkit.integration.enchant.AdvancedEnchantmentsProvider; +import net.momirealms.customfishing.bukkit.integration.enchant.VanillaEnchantmentsProvider; +import net.momirealms.customfishing.bukkit.integration.entity.ItemsAdderEntityProvider; +import net.momirealms.customfishing.bukkit.integration.entity.MythicEntityProvider; +import net.momirealms.customfishing.bukkit.integration.item.*; +import net.momirealms.customfishing.bukkit.integration.level.*; +import net.momirealms.customfishing.bukkit.integration.papi.CompetitionPapi; +import net.momirealms.customfishing.bukkit.integration.papi.CustomFishingPapi; +import net.momirealms.customfishing.bukkit.integration.papi.StatisticsPapi; +import net.momirealms.customfishing.bukkit.integration.quest.BattlePassQuest; +import net.momirealms.customfishing.bukkit.integration.quest.BetonQuestQuest; +import net.momirealms.customfishing.bukkit.integration.quest.ClueScrollsQuest; +import net.momirealms.customfishing.bukkit.integration.season.AdvancedSeasonsProvider; +import net.momirealms.customfishing.bukkit.integration.season.CustomCropsSeasonProvider; +import net.momirealms.customfishing.bukkit.integration.season.RealisticSeasonsProvider; +import net.momirealms.customfishing.bukkit.item.BukkitItemManager; +import net.momirealms.customfishing.common.util.Pair; +import org.bukkit.Bukkit; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class BukkitIntegrationManager implements IntegrationManager { + + private final BukkitCustomFishingPlugin plugin; + private final HashMap levelerProviders = new HashMap<>(); + private final HashMap enchantmentProviders = new HashMap<>(); + private SeasonProvider seasonProvider; + + public BukkitIntegrationManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + this.load(); + } + + @Override + public void disable() { + this.enchantmentProviders.clear(); + this.levelerProviders.clear(); + } + + @Override + public void load() { + registerEnchantmentProvider(new VanillaEnchantmentsProvider()); + if (isHooked("ItemsAdder")) { + registerItemProvider(new ItemsAdderItemProvider()); + registerBlockProvider(new ItemsAdderBlockProvider()); + registerEntityProvider(new ItemsAdderEntityProvider()); + } + if (isHooked("MMOItems")) { + registerItemProvider(new MMOItemsItemProvider()); + } + if (isHooked("Oraxen")) { + registerItemProvider(new OraxenItemProvider()); + registerBlockProvider(new OraxenBlockProvider()); + } + if (isHooked("Zaphkiel")) { + registerItemProvider(new ZaphkielItemProvider()); + } + if (isHooked("NeigeItems")) { + registerItemProvider(new NeigeItemsItemProvider()); + } + if (isHooked("MythicMobs")) { + registerItemProvider(new MythicMobsItemProvider()); + registerEntityProvider(new MythicEntityProvider()); + } + if (isHooked("EcoJobs")) { + registerLevelerProvider(new EcoJobsLevelerProvider()); + } + if (isHooked("EcoSkills")) { + registerLevelerProvider(new EcoSkillsLevelerProvider()); + } + if (isHooked("Jobs")) { + registerLevelerProvider(new JobsRebornLevelerProvider()); + } + if (isHooked("MMOCore")) { + registerLevelerProvider(new MMOCoreLevelerProvider()); + } + if (isHooked("mcMMO")) { + try { + registerItemProvider(new McMMOTreasureProvider()); + } catch (ClassNotFoundException | NoSuchMethodException e) { + plugin.getPluginLogger().warn("Failed to initialize mcMMO Treasure"); + } + registerLevelerProvider(new McMMOLevelerProvider()); + } + if (isHooked("AureliumSkills")) { + registerLevelerProvider(new AureliumSkillsProvider()); + } + if (isHooked("AuraSkills")) { + registerLevelerProvider(new AuraSkillsLevelerProvider()); + } + if (isHooked("AdvancedEnchantments")) { + registerEnchantmentProvider(new AdvancedEnchantmentsProvider()); + } + if (isHooked("RealisticSeasons")) { + registerSeasonProvider(new RealisticSeasonsProvider()); + } else if (isHooked("AdvancedSeasons")) { + registerSeasonProvider(new AdvancedSeasonsProvider()); + } else if (isHooked("CustomCrops")) { + registerSeasonProvider(new CustomCropsSeasonProvider()); + } + if (isHooked("Vault")) { + VaultHook.initialize(); + } + if (isHooked("BattlePass")){ + BattlePassQuest battlePassQuest = new BattlePassQuest(); + battlePassQuest.register(); + } + if (isHooked("ClueScrolls")) { + ClueScrollsQuest clueScrollsQuest = new ClueScrollsQuest(); + clueScrollsQuest.register(); + } + if (isHooked("BetonQuest")) { + BetonQuestQuest.register(); + } + if (isHooked("PlaceholderAPI")) { + new CustomFishingPapi(plugin).load(); + new CompetitionPapi(plugin).load(); + new StatisticsPapi(plugin).load(); + } + } + + private boolean isHooked(String hooked) { + if (Bukkit.getPluginManager().getPlugin(hooked) != null) { + plugin.getPluginLogger().info(hooked + " hooked!"); + return true; + } + return false; + } + + @Override + public boolean registerLevelerProvider(@NotNull LevelerProvider leveler) { + if (levelerProviders.containsKey(leveler.identifier())) return false; + levelerProviders.put(leveler.identifier(), leveler); + return true; + } + + @Override + public boolean unregisterLevelerProvider(@NotNull String id) { + return levelerProviders.remove(id) != null; + } + + @Override + public boolean registerEnchantmentProvider(@NotNull EnchantmentProvider enchantment) { + if (enchantmentProviders.containsKey(enchantment.identifier())) return false; + enchantmentProviders.put(enchantment.identifier(), enchantment); + return true; + } + + @Override + public boolean unregisterEnchantmentProvider(@NotNull String id) { + return enchantmentProviders.remove(id) != null; + } + + @Override + @Nullable + public LevelerProvider getLevelerProvider(String plugin) { + return levelerProviders.get(plugin); + } + + @Override + @Nullable + public EnchantmentProvider getEnchantmentProvider(String id) { + return enchantmentProviders.get(id); + } + + @Override + public List> getEnchantments(ItemStack itemStack) { + ArrayList> list = new ArrayList<>(); + for (EnchantmentProvider enchantmentProvider : enchantmentProviders.values()) { + list.addAll(enchantmentProvider.getEnchants(itemStack)); + } + return list; + } + + @Nullable + @Override + public SeasonProvider getSeasonProvider() { + return seasonProvider; + } + + @Override + public boolean registerSeasonProvider(@NotNull SeasonProvider season) { + if (this.seasonProvider != null) return false; + this.seasonProvider = season; + return true; + } + + @Override + public boolean unregisterSeasonProvider() { + if (this.seasonProvider == null) return false; + this.seasonProvider = null; + return true; + } + + @Override + public boolean registerEntityProvider(@NotNull EntityProvider entity) { + return ((BukkitEntityManager) plugin.getEntityManager()).registerEntityProvider(entity); + } + + @Override + public boolean unregisterEntityProvider(@NotNull String id) { + return ((BukkitEntityManager) plugin.getEntityManager()).unregisterEntityProvider(id); + } + + @Override + public boolean registerItemProvider(@NotNull ItemProvider item) { + return ((BukkitItemManager) plugin.getItemManager()).registerItemProvider(item); + } + + @Override + public boolean unregisterItemProvider(@NotNull String id) { + return ((BukkitItemManager) plugin.getItemManager()).unregisterItemProvider(id); + } + + @Override + public boolean registerBlockProvider(@NotNull BlockProvider block) { + return ((BukkitBlockManager) plugin.getBlockManager()).registerBlockProvider(block); + } + + @Override + public boolean unregisterBlockProvider(@NotNull String id) { + return ((BukkitBlockManager) plugin.getBlockManager()).unregisterBlockProvider(id); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemFactory.java b/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemFactory.java new file mode 100644 index 00000000..4a88371b --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemFactory.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.item; + +import com.saicone.rtag.RtagItem; +import net.momirealms.customfishing.bukkit.item.impl.ComponentItemFactory; +import net.momirealms.customfishing.bukkit.item.impl.UniversalItemFactory; +import net.momirealms.customfishing.common.item.Item; +import net.momirealms.customfishing.common.item.ItemFactory; +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import org.bukkit.inventory.ItemStack; + +import java.util.Objects; +import java.util.Optional; + +public abstract class BukkitItemFactory extends ItemFactory { + + protected BukkitItemFactory(CustomFishingPlugin plugin) { + super(plugin); + } + + public static BukkitItemFactory create(CustomFishingPlugin plugin) { + Objects.requireNonNull(plugin, "plugin"); + switch (plugin.getServerVersion()) { + case "1.17", "1.17.1", + "1.18", "1.18.1", "1.18.2", + "1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4", + "1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4" -> { + return new UniversalItemFactory(plugin); + } + case "1.20.5", "1.20.6", + "1.21", "1.21.1", "1.21.2" -> { + return new ComponentItemFactory(plugin); + } + default -> throw new IllegalStateException("Unsupported server version: " + plugin.getServerVersion()); + } + } + + public Item wrap(ItemStack item) { + Objects.requireNonNull(item, "item"); + return wrap(new RtagItem(item)); + } + + @Override + protected void setTag(RtagItem item, Object value, Object... path) { + item.set(value, path); + } + + @Override + protected Optional getTag(RtagItem item, Object... path) { + return Optional.ofNullable(item.get(path)); + } + + @Override + protected boolean hasTag(RtagItem item, Object... path) { + return item.hasTag(path); + } + + @Override + protected boolean removeTag(RtagItem item, Object... path) { + return item.remove(path); + } + + @Override + protected void update(RtagItem item) { + item.update(); + } + + @Override + protected ItemStack load(RtagItem item) { + return item.load(); + } + + @Override + protected ItemStack getItem(RtagItem item) { + return item.getItem(); + } + + @Override + protected ItemStack loadCopy(RtagItem item) { + return item.loadCopy(); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemManager.java new file mode 100644 index 00000000..ecc5b3d8 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemManager.java @@ -0,0 +1,484 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.item; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.integration.ExternalProvider; +import net.momirealms.customfishing.api.integration.ItemProvider; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.item.CustomFishingItem; +import net.momirealms.customfishing.api.mechanic.item.ItemManager; +import net.momirealms.customfishing.api.util.EventUtils; +import net.momirealms.customfishing.bukkit.integration.item.CustomFishingItemProvider; +import net.momirealms.customfishing.bukkit.item.damage.CustomDurabilityItem; +import net.momirealms.customfishing.bukkit.item.damage.DurabilityItem; +import net.momirealms.customfishing.bukkit.item.damage.VanillaDurabilityItem; +import net.momirealms.customfishing.bukkit.util.ItemStackUtils; +import net.momirealms.customfishing.bukkit.util.LocationUtils; +import net.momirealms.customfishing.common.item.Item; +import net.momirealms.sparrow.heart.SparrowHeart; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.block.Block; +import org.bukkit.block.Skull; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.FishHook; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.*; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.inventory.InventoryPickupItemEvent; +import org.bukkit.event.inventory.PrepareAnvilEvent; +import org.bukkit.event.player.PlayerAttemptPickupItemEvent; +import org.bukkit.event.player.PlayerItemDamageEvent; +import org.bukkit.event.player.PlayerItemMendEvent; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; + +import static java.util.Objects.requireNonNull; + +public class BukkitItemManager implements ItemManager, Listener { + + private final BukkitCustomFishingPlugin plugin; + private final HashMap itemProviders = new HashMap<>(); + private final HashMap items = new HashMap<>(); + private final BukkitItemFactory factory; + private ItemProvider[] itemDetectArray; + + public BukkitItemManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + this.factory = BukkitItemFactory.create(plugin); + this.registerItemProvider(new ItemProvider() { + @NotNull + @Override + public ItemStack buildItem(@NotNull Player player, @NotNull String id) { + return new ItemStack(Material.valueOf(id.toUpperCase(Locale.ENGLISH))); + } + @NotNull + @Override + public String itemID(@NotNull ItemStack itemStack) { + return itemStack.getType().name(); + } + @Override + public String identifier() { + return "vanilla"; + } + }); + this.registerItemProvider(new CustomFishingItemProvider()); + } + + @Override + public void unload() { + HandlerList.unregisterAll(this); + this.items.clear(); + } + + @Override + public void load() { + Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap()); + this.resetItemDetectionOrder(); + for (ItemProvider provider : itemProviders.values()) { + plugin.debug("Registered ItemProvider: " + provider.identifier()); + } + plugin.debug("Loaded " + items.size() + " items"); + plugin.debug("Item order: " + Arrays.toString(Arrays.stream(itemDetectArray).map(ExternalProvider::identifier).toList().toArray(new String[0]))); + } + + @Override + public boolean registerItem(@NotNull CustomFishingItem item) { + if (items.containsKey(item.id())) return false; + items.put(item.id(), item); + return true; + } + + @Nullable + @Override + public ItemStack buildInternal(@NotNull Context context, @NotNull String id) { +// CustomFishingItem item = requireNonNull(items.get(id), () -> "No item found for " + id); + CustomFishingItem item = items.get(id); + if (item == null) return null; + return build(context, item); + } + + @NotNull + @Override + public ItemStack build(@NotNull Context context, @NotNull CustomFishingItem item) { + ItemStack itemStack = getOriginalStack(context.getHolder(), item.material()); + if (itemStack.getType() == Material.AIR) return itemStack; + Item wrappedItemStack = factory.wrap(itemStack); + for (BiConsumer, Context> consumer : item.tagConsumers()) { + consumer.accept(wrappedItemStack, context); + } + return wrappedItemStack.load(); + } + + @Override + public ItemStack buildAny(@NotNull Context context, @NotNull String item) { + return getOriginalStack(context.getHolder(), item); + } + + @NotNull + @Override + public String getItemID(@NotNull ItemStack itemStack) { + if (itemStack.getType() == Material.AIR) + return "AIR"; + for (ItemProvider library : itemDetectArray) { + String id = library.itemID(itemStack); + if (id != null) + return id; + } + // should not reach this because vanilla library would always work + return "AIR"; + } + + @Override + public String getCustomFishingItemID(@NotNull ItemStack itemStack) { + return (String) factory.wrap(itemStack).getTag("CustomFishing", "id").orElse(null); + } + + @Nullable + @Override + public org.bukkit.entity.Item dropItemLoot(@NotNull Context context, ItemStack rod, FishHook hook) { + String id = requireNonNull(context.arg(ContextKeys.ID)); + ItemStack itemStack; + if (id.equals("vanilla")) { + itemStack = SparrowHeart.getInstance().getFishingLoot(context.getHolder(), hook, rod).stream().findAny().orElseThrow(() -> new RuntimeException("new EntityItem would throw if for whatever reason (mostly shitty datapacks) the fishing loot turns out to be empty")); + } else { + itemStack = requireNonNull(buildInternal(context, id)); + } + + if (itemStack.getType() == Material.AIR) { + return null; + } + + Player player = context.getHolder(); + Location playerLocation = player.getLocation(); + Location hookLocation = requireNonNull(context.arg(ContextKeys.OTHER_LOCATION)); + + double d0 = playerLocation.getX() - hookLocation.getX(); + double d1 = playerLocation.getY() - hookLocation.getY(); + double d2 = playerLocation.getZ() - hookLocation.getZ(); + Vector vector = new Vector(d0 * 0.1D, d1 * 0.1D + Math.sqrt(Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2)) * 0.08D, d2 * 0.1D); + + org.bukkit.entity.Item itemEntity = hookLocation.getWorld().dropItem(hookLocation, itemStack); + + itemEntity.setInvulnerable(true); + // prevent from being killed by lava + plugin.getScheduler().asyncLater(() -> { + if (itemEntity.isValid()) + itemEntity.setInvulnerable(false); + }, 1, TimeUnit.SECONDS); + + itemEntity.setVelocity(vector); + + return itemEntity; + } + + private ItemStack getOriginalStack(Player player, String material) { + if (!material.contains(":")) { + try { + return new ItemStack(Material.valueOf(material.toUpperCase(Locale.ENGLISH))); + } catch (IllegalArgumentException e) { + plugin.getPluginLogger().severe("material " + material + " not exists", e); + return new ItemStack(Material.PAPER); + } + } else { + String[] split = material.split(":", 2); + ItemProvider provider = requireNonNull(itemProviders.get(split[0]), "Item provider: " + split[0] + " not found"); + return requireNonNull(provider.buildItem(player, split[0]), "Item: " + split[0] + " not found"); + } + } + + private void resetItemDetectionOrder() { + ArrayList list = new ArrayList<>(); + for (String plugin : ConfigManager.itemDetectOrder()) { + ItemProvider provider = itemProviders.get(plugin); + if (provider != null) + list.add(provider); + } + this.itemDetectArray = list.toArray(new ItemProvider[0]); + } + + public boolean registerItemProvider(ItemProvider item) { + if (itemProviders.containsKey(item.identifier())) return false; + itemProviders.put(item.identifier(), item); + this.resetItemDetectionOrder(); + return true; + } + + public boolean unregisterItemProvider(String id) { + boolean success = itemProviders.remove(id) != null; + if (success) + this.resetItemDetectionOrder(); + return success; + } + + @Override + public boolean hasCustomDurability(ItemStack itemStack) { + if (itemStack == null || itemStack.getType() == Material.AIR || itemStack.getAmount() == 0) + return false; + Item wrapped = factory.wrap(itemStack); + return wrapped.hasTag("CustomFishing", "max_dur"); + } + + @Override + public void decreaseDurability(Player player, ItemStack itemStack, int amount, boolean incorrectUsage) { + if (itemStack == null || itemStack.getType() == Material.AIR || itemStack.getAmount() == 0) + return; + if (!incorrectUsage) { + int unBreakingLevel = itemStack.getEnchantmentLevel(Enchantment.DURABILITY); + if (Math.random() > (double) 1 / (unBreakingLevel + 1)) { + return; + } + } + Item wrapped = factory.wrap(itemStack); + if (wrapped.unbreakable()) + return; + + ItemMeta previousMeta = itemStack.getItemMeta().clone(); + PlayerItemDamageEvent itemDamageEvent = new PlayerItemDamageEvent(player, itemStack, amount); + if (EventUtils.fireAndCheckCancel(itemDamageEvent)) { + plugin.debug("Another plugin modified the item from `PlayerItemDamageEvent` called by CustomFishing"); + return; + } + if (!itemStack.getItemMeta().equals(previousMeta)) { + return; + } + + DurabilityItem durabilityItem = wrapDurabilityItem(wrapped); + int damage = durabilityItem.damage(); + if (damage + amount >= durabilityItem.maxDamage()) { + plugin.getSenderFactory().getAudience(player).playSound(Sound.sound(Key.key("minecraft:entity.item.break"), Sound.Source.PLAYER, 1, 1)); + itemStack.setAmount(0); + return; + } + + durabilityItem.damage(damage + amount); + wrapped.load(); + } + + @Override + public void setDurability(Player player, ItemStack itemStack, int damage) { + if (itemStack == null || itemStack.getType() == Material.AIR || itemStack.getAmount() == 0) + return; + Item wrapped = factory.wrap(itemStack); + if (wrapped.unbreakable()) + return; + DurabilityItem wrappedDurability = wrapDurabilityItem(wrapped); + if (damage >= wrappedDurability.maxDamage()) { + if (player != null) + plugin.getSenderFactory().getAudience(player).playSound(Sound.sound(Key.key("minecraft:entity.item.break"), Sound.Source.PLAYER, 1, 1)); + itemStack.setAmount(0); + return; + } + wrappedDurability.damage(damage); + wrapped.load(); + } + + public DurabilityItem wrapDurabilityItem(Item wrapped) { + if (wrapped.hasTag("CustomFishing", "max_dur")) { + return new CustomDurabilityItem(wrapped); + } else { + return new VanillaDurabilityItem(wrapped); + } + } + + @EventHandler (ignoreCancelled = true) + public void onMending(PlayerItemMendEvent event) { + ItemStack itemStack = event.getItem(); + if (!hasCustomDurability(itemStack)) { + return; + } + event.setCancelled(true); + Item wrapped = factory.wrap(itemStack); + if (wrapped.unbreakable()) + return; + DurabilityItem wrappedDurability = wrapDurabilityItem(wrapped); + setDurability(event.getPlayer(), itemStack, Math.max(wrappedDurability.damage() - event.getRepairAmount(), 0)); + } + + @EventHandler (ignoreCancelled = true) + public void onAnvil(PrepareAnvilEvent event) { + AnvilInventory anvil = event.getInventory(); + ItemStack first = anvil.getFirstItem(); + ItemStack second = anvil.getSecondItem(); + if (first != null && second != null + && first.getType() == Material.FISHING_ROD && second.getType() == Material.FISHING_ROD && event.getResult() != null + && hasCustomDurability(first)) { + Item wrapped1 = factory.wrap(anvil.getResult()); + DurabilityItem wrappedDurability1 = wrapDurabilityItem(wrapped1); + + Item wrapped2 = factory.wrap(second); + DurabilityItem wrappedDurability2 = wrapDurabilityItem(wrapped2); + + int durability2 = wrappedDurability2.maxDamage() - wrappedDurability2.damage(); + int damage1 = Math.max(wrappedDurability1.damage() - durability2, 0); + wrappedDurability1.damage(damage1); + event.setResult(wrapped1.load()); + } + } + + @EventHandler(ignoreCancelled = true) + public void onInvPickItem(InventoryPickupItemEvent event) { + ItemStack itemStack = event.getItem().getItemStack(); + Item wrapped = factory.wrap(itemStack); + if (wrapped.hasTag("owner")) { + wrapped.removeTag("owner"); + itemStack.setItemMeta(wrapped.getItem().getItemMeta()); + } + } + + @EventHandler (ignoreCancelled = true) + public void onPlaceBlock(BlockPlaceEvent event) { + ItemStack itemStack = event.getItemInHand(); + if (itemStack.getType() == Material.AIR || itemStack.getAmount() == 0 || !itemStack.hasItemMeta()) { + return; + } + + Item wrapped = factory.wrap(itemStack); + if (wrapped.hasTag("CustomFishing")) { + if (!wrapped.hasTag("CustomFishing", "placeable") || ((int) wrapped.getTag("CustomFishing", "placeable").get()) != 1) { + event.setCancelled(true); + return; + } + Block block = event.getBlock(); + if (block.getState() instanceof Skull) { + PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer(); + ItemStack cloned = itemStack.clone(); + cloned.setAmount(1); + pdc.set(new NamespacedKey(plugin.getBoostrap(), LocationUtils.toChunkPosString(block.getLocation())), PersistentDataType.STRING, ItemStackUtils.toBase64(cloned)); + } else { + event.setCancelled(true); + } + } + } + + @EventHandler (ignoreCancelled = true) + public void onBreakBlock(BlockBreakEvent event) { + final Block block = event.getBlock(); + if (block.getState() instanceof Skull) { + PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer(); + String base64 = pdc.get(new NamespacedKey(plugin.getBoostrap(), LocationUtils.toChunkPosString(block.getLocation())), PersistentDataType.STRING); + if (base64 != null) { + ItemStack itemStack = ItemStackUtils.fromBase64(base64); + event.setDropItems(false); + block.getLocation().getWorld().dropItemNaturally(block.getLocation(), itemStack); + } + } + } + + @EventHandler (ignoreCancelled = true) + public void onPiston(BlockPistonExtendEvent event) { + handlePiston(event, event.getBlocks()); + } + + @EventHandler (ignoreCancelled = true) + public void onPiston(BlockPistonRetractEvent event) { + handlePiston(event, event.getBlocks()); + } + + private void handlePiston(Cancellable event, List blockList) { + for (Block block : blockList) { + if (block.getState() instanceof Skull) { + PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer(); + if (pdc.has(new NamespacedKey(plugin.getBoostrap(), LocationUtils.toChunkPosString(block.getLocation())), PersistentDataType.STRING)) { + event.setCancelled(true); + return; + } + } + } + } + + @EventHandler (ignoreCancelled = true) + public void onExplosion(BlockExplodeEvent event) { + handleExplosion(event.blockList()); + } + + @EventHandler (ignoreCancelled = true) + public void onExplosion(EntityExplodeEvent event) { + handleExplosion(event.blockList()); + } + + @EventHandler (ignoreCancelled = true) + public void onPickUpItem(PlayerAttemptPickupItemEvent event) { + String owner = event.getItem().getPersistentDataContainer().get(requireNonNull(NamespacedKey.fromString("owner", plugin.getBoostrap())), PersistentDataType.STRING); + if (owner != null) { + if (!owner.equals(event.getPlayer().getName())) { + event.setCancelled(true); + } + } + } + + private void handleExplosion(List blocks) { + ArrayList blockToRemove = new ArrayList<>(); + for (Block block : blocks) { + if (block.getState() instanceof Skull) { + PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer(); + var nk = new NamespacedKey(plugin.getBoostrap(), LocationUtils.toChunkPosString(block.getLocation())); + String base64 = pdc.get(nk, PersistentDataType.STRING); + if (base64 != null) { + ItemStack itemStack = ItemStackUtils.fromBase64(base64); + block.getLocation().getWorld().dropItemNaturally(block.getLocation(), itemStack); + blockToRemove.add(block); + block.setType(Material.AIR); + pdc.remove(nk); + } + } + } + blocks.removeAll(blockToRemove); + } + + @Override + public BukkitItemFactory getFactory() { + return factory; + } + + @Override + public ItemProvider[] getItemProviders() { + return itemProviders.values().toArray(new ItemProvider[0]); + } + + @Override + public Collection getItemIDs() { + return items.keySet(); + } + + @Override + public Item wrap(ItemStack itemStack) { + return factory.wrap(itemStack); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/item/damage/CustomDurabilityItem.java b/core/src/main/java/net/momirealms/customfishing/bukkit/item/damage/CustomDurabilityItem.java new file mode 100644 index 00000000..5028cbf4 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/item/damage/CustomDurabilityItem.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.item.damage; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ScoreComponent; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import net.momirealms.customfishing.common.item.Item; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; + +public class CustomDurabilityItem implements DurabilityItem { + + private final Item item; + + public CustomDurabilityItem(Item item) { + this.item = item; + } + + @Override + public void damage(int value) { + int customMaxDamage = (int) item.getTag("CustomFishing", "max_dur").get(); + int maxDamage = item.maxDamage().get(); + double ratio = (double) maxDamage / (double) customMaxDamage; + int fakeDamage = (int) (value * ratio); + item.damage(fakeDamage); + item.setTag(customMaxDamage - value, "CustomFishing", "cur_dur"); + List durabilityLore = ConfigManager.durabilityLore(); + List previousLore = item.lore().orElse(new ArrayList<>()); + List newLore = new ArrayList<>(); + for (String previous : previousLore) { + Component component = AdventureHelper.jsonToComponent(previous); + if (component instanceof ScoreComponent scoreComponent && scoreComponent.name().equals("cf")) { + if (scoreComponent.objective().equals("durability")) { + continue; + } + } + newLore.add(previous); + } + for (String lore : durabilityLore) { + ScoreComponent.Builder builder = Component.score().name("cf").objective("durability"); + builder.append(AdventureHelper.miniMessage(lore.replace("{dur}", String.valueOf(customMaxDamage - value)).replace("{max}", String.valueOf(customMaxDamage)))); + newLore.add(AdventureHelper.componentToJson(builder.build())); + } + item.lore(newLore); + } + + @Override + public int damage() { + int customMaxDamage = (int) item.getTag("CustomFishing", "max_dur").get(); + return customMaxDamage - (int) item.getTag("CustomFishing", "cur_dur").orElse(0); + } + + @Override + public int maxDamage() { + return (int) item.getTag("CustomFishing", "max_dur").get(); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/item/damage/DurabilityItem.java b/core/src/main/java/net/momirealms/customfishing/bukkit/item/damage/DurabilityItem.java new file mode 100644 index 00000000..944f7d4a --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/item/damage/DurabilityItem.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.item.damage; + +public interface DurabilityItem { + + void damage(int value); + + int damage(); + + int maxDamage(); +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/VanillaItemImpl.java b/core/src/main/java/net/momirealms/customfishing/bukkit/item/damage/VanillaDurabilityItem.java similarity index 58% rename from plugin/src/main/java/net/momirealms/customfishing/compatibility/item/VanillaItemImpl.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/item/damage/VanillaDurabilityItem.java index d502b47a..e22db0e7 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/VanillaItemImpl.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/item/damage/VanillaDurabilityItem.java @@ -15,29 +15,31 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.compatibility.item; +package net.momirealms.customfishing.bukkit.item.damage; -import net.momirealms.customfishing.api.mechanic.item.ItemLibrary; -import org.bukkit.Material; -import org.bukkit.entity.Player; +import net.momirealms.customfishing.common.item.Item; import org.bukkit.inventory.ItemStack; -import java.util.Locale; +public class VanillaDurabilityItem implements DurabilityItem { -public class VanillaItemImpl implements ItemLibrary { + private final Item item; - @Override - public String identification() { - return "vanilla"; + public VanillaDurabilityItem(Item item) { + this.item = item; } @Override - public ItemStack buildItem(Player player, String id) { - return new ItemStack(Material.valueOf(id.toUpperCase(Locale.ENGLISH))); + public void damage(int value) { + item.damage(value); } @Override - public String getItemID(ItemStack itemStack) { - return itemStack.getType().name(); + public int damage() { + return item.damage().orElse(0); + } + + @Override + public int maxDamage() { + return item.maxDamage().get(); } } diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/item/impl/ComponentItemFactory.java b/core/src/main/java/net/momirealms/customfishing/bukkit/item/impl/ComponentItemFactory.java new file mode 100644 index 00000000..d6103dca --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/item/impl/ComponentItemFactory.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.item.impl; + +import com.saicone.rtag.RtagItem; +import com.saicone.rtag.data.ComponentType; +import net.momirealms.customfishing.bukkit.item.BukkitItemFactory; +import net.momirealms.customfishing.common.item.ComponentKeys; +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import net.momirealms.customfishing.common.util.Key; +import net.momirealms.sparrow.heart.SparrowHeart; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@SuppressWarnings("UnstableApiUsage") +public class ComponentItemFactory extends BukkitItemFactory { + + public ComponentItemFactory(CustomFishingPlugin plugin) { + super(plugin); + } + + @Override + protected void customModelData(RtagItem item, Integer data) { + if (data == null) { + item.removeComponent(ComponentKeys.CUSTOM_MODEL_DATA); + } else { + item.setComponent(ComponentKeys.CUSTOM_MODEL_DATA, data); + } + } + + @Override + protected Optional customModelData(RtagItem item) { + if (!item.hasComponent(ComponentKeys.CUSTOM_MODEL_DATA)) return Optional.empty(); + return Optional.ofNullable( + (Integer) ComponentType.encodeJava( + ComponentKeys.CUSTOM_MODEL_DATA, + item.getComponent(ComponentKeys.CUSTOM_MODEL_DATA) + ).orElse(null) + ); + } + + @Override + protected void displayName(RtagItem item, String json) { + if (json == null) { + item.removeComponent(ComponentKeys.CUSTOM_NAME); + } else { + item.setComponent(ComponentKeys.CUSTOM_NAME, json); + } + } + + @Override + protected Optional displayName(RtagItem item) { + if (!item.hasComponent(ComponentKeys.CUSTOM_NAME)) return Optional.empty(); + return Optional.ofNullable( + (String) ComponentType.encodeJava( + ComponentKeys.CUSTOM_NAME, + item.getComponent(ComponentKeys.CUSTOM_NAME) + ).orElse(null) + ); + } + + @Override + protected void skull(RtagItem item, String skullData) { + final Map profile = Map.of( + "properties", List.of( + Map.of( + "name", "textures", + "value", skullData + ) + ) + ); + item.setComponent("minecraft:profile", profile); + } + + @SuppressWarnings("unchecked") + @Override + protected Optional> lore(RtagItem item) { + if (!item.hasComponent(ComponentKeys.LORE)) return Optional.empty(); + return Optional.ofNullable( + (List) ComponentType.encodeJava( + ComponentKeys.LORE, + item.getComponent(ComponentKeys.LORE) + ).orElse(null) + ); + } + + @Override + protected void lore(RtagItem item, List lore) { + if (lore == null || lore.isEmpty()) { + item.removeComponent(ComponentKeys.LORE); + } else { + item.setComponent(ComponentKeys.LORE, lore); + } + } + + @Override + protected boolean unbreakable(RtagItem item) { + return item.isUnbreakable(); + } + + @Override + protected void unbreakable(RtagItem item, boolean unbreakable) { + item.setUnbreakable(unbreakable); + } + + @Override + protected Optional glint(RtagItem item) { + return Optional.ofNullable((Boolean) item.getComponent(ComponentKeys.ENCHANTMENT_GLINT_OVERRIDE)); + } + + @Override + protected void glint(RtagItem item, Boolean glint) { + item.setComponent(ComponentKeys.ENCHANTMENT_GLINT_OVERRIDE, glint); + } + + @Override + protected Optional damage(RtagItem item) { + if (!item.hasComponent(ComponentKeys.DAMAGE)) return Optional.empty(); + return Optional.ofNullable( + (Integer) ComponentType.encodeJava( + ComponentKeys.DAMAGE, + item.getComponent(ComponentKeys.DAMAGE) + ).orElse(null) + ); + } + + @Override + protected void damage(RtagItem item, Integer damage) { + if (damage == null) damage = 0; + item.setComponent(ComponentKeys.DAMAGE, damage); + } + + @Override + protected Optional maxDamage(RtagItem item) { + if (!item.hasComponent(ComponentKeys.MAX_DAMAGE)) return Optional.of((int) item.getItem().getType().getMaxDurability()); + return Optional.ofNullable( + (Integer) ComponentType.encodeJava( + ComponentKeys.MAX_DAMAGE, + item.getComponent(ComponentKeys.MAX_DAMAGE) + ).orElse(null) + ); + } + + @Override + protected void maxDamage(RtagItem item, Integer damage) { + if (damage == null) { + item.removeComponent(ComponentKeys.MAX_DAMAGE); + } else { + item.setComponent(ComponentKeys.MAX_DAMAGE, damage); + } + } + + @Override + protected void enchantments(RtagItem item, Map enchantments) { + Map enchants = new HashMap<>(); + for (Map.Entry entry : enchantments.entrySet()) { + enchants.put(entry.getKey().toString(), Integer.valueOf(entry.getValue())); + } + item.setComponent(ComponentKeys.ENCHANTMENTS, enchants); + } + + @Override + protected void storedEnchantments(RtagItem item, Map enchantments) { + Map enchants = new HashMap<>(); + for (Map.Entry entry : enchantments.entrySet()) { + enchants.put(entry.getKey().toString(), Integer.valueOf(entry.getValue())); + } + item.setComponent(ComponentKeys.STORED_ENCHANTMENTS, enchants); + } + + @Override + protected void addEnchantment(RtagItem item, Key enchantment, int level) { + Object enchant = item.getComponent(ComponentKeys.ENCHANTMENTS); + Map map = SparrowHeart.getInstance().itemEnchantmentsToMap(enchant); + map.put(enchantment.toString(), level); + item.setComponent(ComponentKeys.ENCHANTMENTS, map); + } + + @Override + protected void addStoredEnchantment(RtagItem item, Key enchantment, int level) { + Object enchant = item.getComponent(ComponentKeys.STORED_ENCHANTMENTS); + Map map = SparrowHeart.getInstance().itemEnchantmentsToMap(enchant); + map.put(enchantment.toString(), level); + item.setComponent(ComponentKeys.STORED_ENCHANTMENTS, map); + } + + @Override + protected void itemFlags(RtagItem item, List flags) { + throw new UnsupportedOperationException("This feature is not available on 1.20.5+"); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/item/impl/UniversalItemFactory.java b/core/src/main/java/net/momirealms/customfishing/bukkit/item/impl/UniversalItemFactory.java new file mode 100644 index 00000000..0f8d8638 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/item/impl/UniversalItemFactory.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.item.impl; + +import com.saicone.rtag.RtagItem; +import com.saicone.rtag.tag.TagBase; +import com.saicone.rtag.tag.TagCompound; +import com.saicone.rtag.tag.TagList; +import net.momirealms.customfishing.bukkit.item.BukkitItemFactory; +import net.momirealms.customfishing.common.plugin.CustomFishingPlugin; +import net.momirealms.customfishing.common.util.Key; +import org.bukkit.inventory.ItemFlag; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class UniversalItemFactory extends BukkitItemFactory { + + public UniversalItemFactory(CustomFishingPlugin plugin) { + super(plugin); + } + + @Override + protected void displayName(RtagItem item, String json) { + if (json != null) { + item.set(json, "display", "Name"); + } else { + item.remove("display", "Name"); + } + } + + @Override + protected Optional displayName(RtagItem item) { + if (!item.hasTag("display", "Name")) return Optional.empty(); + return Optional.of(item.get("display", "Name")); + } + + @Override + protected void customModelData(RtagItem item, Integer data) { + if (data == null) { + item.remove("CustomModelData"); + } else { + item.set(data, "CustomModelData"); + } + } + + @Override + protected Optional customModelData(RtagItem item) { + if (!item.hasTag("CustomModelData")) return Optional.empty(); + return Optional.of(item.get("CustomModelData")); + } + + @Override + protected void skull(RtagItem item, String skullData) { + if (skullData == null) { + item.remove("SkullOwner"); + } else { + item.set(List.of(Map.of("Value", skullData)), "SkullOwner", "Properties", "textures"); + } + } + + @Override + protected Optional> lore(RtagItem item) { + if (!item.hasTag("display", "Lore")) return Optional.empty(); + return Optional.of(item.get("display", "Lore")); + } + + @Override + protected void lore(RtagItem item, List lore) { + if (lore == null || lore.isEmpty()) { + item.remove("display", "Lore"); + } else { + item.set(lore, "display", "Lore"); + } + } + + @Override + protected boolean unbreakable(RtagItem item) { + return item.isUnbreakable(); + } + + @Override + protected void unbreakable(RtagItem item, boolean unbreakable) { + item.setUnbreakable(unbreakable); + } + + @Override + protected Optional glint(RtagItem item) { + return Optional.of(false); + } + + @Override + protected void glint(RtagItem item, Boolean glint) { + throw new UnsupportedOperationException("This feature is only available on 1.20.5+"); + } + + @Override + protected Optional damage(RtagItem item) { + if (!item.hasTag("Damage")) return Optional.empty(); + return Optional.of(item.get("Damage")); + } + + @Override + protected void damage(RtagItem item, Integer damage) { + item.set(damage, "Damage"); + } + + @Override + protected Optional maxDamage(RtagItem item) { +// if (!item.hasTag("CustomFishing", "max_dur")) return Optional.empty(); +// return Optional.of(item.get("CustomFishing", "max_dur")); + return Optional.of((int) item.getItem().getType().getMaxDurability()); + } + + @Override + protected void maxDamage(RtagItem item, Integer damage) { +// if (damage == null) { +// item.remove("CustomFishing", "max_dur"); +// } else { +// item.set(damage, "CustomFishing", "max_dur"); +// } + throw new UnsupportedOperationException("This feature is only available on 1.20.5+"); + } + + @Override + protected void enchantments(RtagItem item, Map enchantments) { + ArrayList tags = new ArrayList<>(); + for (Map.Entry entry : enchantments.entrySet()) { + tags.add((Map.of("id", entry.getKey().toString(), "lvl", entry.getValue()))); + } + item.set(tags, "Enchantments"); + } + + @Override + protected void storedEnchantments(RtagItem item, Map enchantments) { + ArrayList tags = new ArrayList<>(); + for (Map.Entry entry : enchantments.entrySet()) { + tags.add((Map.of("id", entry.getKey().toString(), "lvl", entry.getValue()))); + } + item.set(tags, "StoredEnchantments"); + } + + @Override + protected void addEnchantment(RtagItem item, Key enchantment, int level) { + Object enchantments = item.getExact("Enchantments"); + if (enchantments != null) { + for (Object enchant : TagList.getValue(enchantments)) { + if (TagBase.getValue(TagCompound.get(enchant, "id")).equals(enchant.toString())) { + TagCompound.set(enchant, "lvl", TagBase.newTag(level)); + return; + } + } + item.add(Map.of("id", enchantment.toString(), "lvl", (short) level), "Enchantments"); + } else { + item.set(List.of(Map.of("id", enchantment.toString(), "lvl", (short) level)), "Enchantments"); + } + } + + @Override + protected void addStoredEnchantment(RtagItem item, Key enchantment, int level) { + Object enchantments = item.getExact("StoredEnchantments"); + if (enchantments != null) { + for (Object enchant : TagList.getValue(enchantments)) { + if (TagBase.getValue(TagCompound.get(enchant, "id")).equals(enchant.toString())) { + TagCompound.set(enchant, "lvl", TagBase.newTag(level)); + return; + } + } + item.add(Map.of("id", enchantment.toString(), "lvl", (short) level), "StoredEnchantments"); + } else { + item.set(List.of(Map.of("id", enchantment.toString(), "lvl", (short) level)), "StoredEnchantments"); + } + } + + @Override + protected void itemFlags(RtagItem item, List flags) { + if (flags == null || flags.isEmpty()) { + item.remove("HideFlags"); + return; + } + int f = 0; + for (String flag : flags) { + ItemFlag itemFlag = ItemFlag.valueOf(flag); + f = f | 1 << itemFlag.ordinal(); + } + item.set(f, "HideFlags"); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/loot/BukkitLootManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/loot/BukkitLootManager.java new file mode 100644 index 00000000..baf31ac1 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/loot/BukkitLootManager.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.loot; + +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.effect.Effect; +import net.momirealms.customfishing.api.mechanic.loot.Loot; +import net.momirealms.customfishing.api.mechanic.loot.LootManager; +import net.momirealms.customfishing.api.mechanic.requirement.ConditionalElement; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager; +import net.momirealms.customfishing.common.util.Pair; +import net.momirealms.customfishing.common.util.WeightUtils; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.*; +import java.util.function.BiFunction; + +@SuppressWarnings("DuplicatedCode") +public class BukkitLootManager implements LootManager { + + private final BukkitCustomFishingPlugin plugin; + private final HashMap lootMap = new HashMap<>(); + private final HashMap> groupMembersMap = new HashMap<>(); + private final LinkedHashMap, Double, Double>>>, Player>> lootConditions = new LinkedHashMap<>(); + + public BukkitLootManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void unload() { + this.lootMap.clear(); + this.groupMembersMap.clear(); + this.lootConditions.clear(); + } + + @Override + public void load() { + plugin.debug("Loaded " + lootMap.size() + " loots"); + for (Map.Entry> entry : groupMembersMap.entrySet()) { + plugin.debug("Group: {" + entry.getKey() + "} Members: " + entry.getValue()); + } + File file = new File(plugin.getDataFolder(), "loot-conditions.yml"); + if (!file.exists()) { + plugin.getBoostrap().saveResource("loot-conditions.yml", false); + } + YamlDocument lootConditionsConfig = plugin.getConfigManager().loadData(file); + for (Map.Entry entry : lootConditionsConfig.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section section) { + lootConditions.put(entry.getKey(), parseLootConditions(section)); + } + } + } + + private ConditionalElement, Double, Double>>>, Player> parseLootConditions(Section section) { + Section subSection = section.getSection("sub-groups"); + if (subSection == null) { + return new ConditionalElement<>( + plugin.getConfigManager().parseWeightOperation(section.getStringList("list")), + Map.of(), + plugin.getRequirementManager().parseRequirements(section.getSection("conditions"), false) + ); + } else { + HashMap, Double, Double>>>, Player>> subElements = new HashMap<>(); + for (Map.Entry entry : subSection.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + subElements.put(entry.getKey(), parseLootConditions(innerSection)); + } + } + return new ConditionalElement<>( + plugin.getConfigManager().parseWeightOperation(section.getStringList("list")), + subElements, + plugin.getRequirementManager().parseRequirements(section.getSection("conditions"), false) + ); + } + } + + @Override + public boolean registerLoot(@NotNull Loot loot) { + if (lootMap.containsKey(loot.id())) return false; + this.lootMap.put(loot.id(), loot); + for (String group : loot.lootGroup()) { + addGroupMember(group, loot.id()); + } + return true; + } + + private void addGroupMember(String group, String member) { + List members = groupMembersMap.get(group); + if (members == null) { + members = new ArrayList<>(List.of(member)); + groupMembersMap.put(group, members); + } else { + members.add(member); + } + } + + @NotNull + @Override + public List getGroupMembers(String key) { + return Optional.ofNullable(groupMembersMap.get(key)).orElse(List.of()); + } + + @NotNull + @Override + public Optional getLoot(String key) { + return Optional.ofNullable(lootMap.get(key)); + } + + @Override + public HashMap getWeightedLoots(Effect effect, Context context) { + HashMap lootWeightMap = new HashMap<>(); + for (ConditionalElement, Double, Double>>>, Player> conditionalElement : lootConditions.values()) { + modifyWeightMap(lootWeightMap, context, conditionalElement); + } + for (Pair, Double, Double>> pair : effect.weightOperations()) { + double previous = lootWeightMap.getOrDefault(pair.left(), 0d); + if (previous > 0) + lootWeightMap.put(pair.left(), pair.right().apply(context, previous)); + } + for (Pair, Double, Double>> pair : effect.weightOperationsIgnored()) { + double previous = lootWeightMap.getOrDefault(pair.left(), 0d); + lootWeightMap.put(pair.left(), pair.right().apply(context, previous)); + } + return lootWeightMap; + } + + @Nullable + @Override + public Loot getNextLoot(Effect effect, Context context) { + HashMap lootWeightMap = new HashMap<>(); + for (ConditionalElement, Double, Double>>>, Player> conditionalElement : lootConditions.values()) { + modifyWeightMap(lootWeightMap, context, conditionalElement); + } + for (Pair, Double, Double>> pair : effect.weightOperations()) { + double previous = lootWeightMap.getOrDefault(pair.left(), 0d); + if (previous > 0) + lootWeightMap.put(pair.left(), pair.right().apply(context, previous)); + } + for (Pair, Double, Double>> pair : effect.weightOperationsIgnored()) { + double previous = lootWeightMap.getOrDefault(pair.left(), 0d); + lootWeightMap.put(pair.left(), pair.right().apply(context, previous)); + } + String lootID = WeightUtils.getRandom(lootWeightMap); + return Optional.ofNullable(lootID) + .map(id -> getLoot(lootID).orElseThrow(() -> new RuntimeException("Could not find loot " + lootID))) + .orElseThrow(() -> new RuntimeException("No loot available. " + context)); + } + + private void modifyWeightMap(Map weightMap, Context context, ConditionalElement, Double, Double>>>, Player> conditionalElement) { + if (conditionalElement == null) return; + if (RequirementManager.isSatisfied(context, conditionalElement.getRequirements())) { + for (Pair, Double, Double>> modifierPair : conditionalElement.getElement()) { + double previous = weightMap.getOrDefault(modifierPair.left(), 0d); + weightMap.put(modifierPair.left(), modifierPair.right().apply(context, previous)); + } + for (ConditionalElement, Double, Double>>>, Player> sub : conditionalElement.getSubElements().values()) { + modifyWeightMap(weightMap, context, sub); + } + } + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/market/BukkitMarketManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/market/BukkitMarketManager.java new file mode 100644 index 00000000..d6ab950b --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/market/BukkitMarketManager.java @@ -0,0 +1,528 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.market; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.action.ActionManager; +import net.momirealms.customfishing.api.mechanic.config.GUIItemParser; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.item.CustomFishingItem; +import net.momirealms.customfishing.api.mechanic.market.MarketGUIHolder; +import net.momirealms.customfishing.api.mechanic.market.MarketManager; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import net.momirealms.customfishing.api.mechanic.misc.value.TextValue; +import net.momirealms.customfishing.api.storage.data.EarningData; +import net.momirealms.customfishing.api.storage.user.UserData; +import net.momirealms.customfishing.bukkit.config.BukkitConfigManager; +import net.momirealms.customfishing.bukkit.item.BukkitItemFactory; +import net.momirealms.customfishing.common.item.Item; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customfishing.common.util.Pair; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.ShulkerBox; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.*; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BlockStateMeta; +import org.bukkit.inventory.meta.BundleMeta; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +@SuppressWarnings("DuplicatedCode") +public class BukkitMarketManager implements MarketManager, Listener { + + private final BukkitCustomFishingPlugin plugin; + + private final HashMap> priceMap; + private String formula; + private MathValue earningsLimit; + private boolean allowItemWithNoPrice; + protected boolean sellFishingBag; + + protected TextValue title; + protected String[] layout; + protected final HashMap decorativeIcons; + protected final ConcurrentHashMap marketGUICache; + + protected char itemSlot; + protected char sellSlot; + protected char sellAllSlot; + + protected CustomFishingItem sellIconAllowItem; + protected CustomFishingItem sellIconDenyItem; + protected CustomFishingItem sellIconLimitItem; + protected CustomFishingItem sellAllIconAllowItem; + protected CustomFishingItem sellAllIconDenyItem; + protected CustomFishingItem sellAllIconLimitItem; + protected Action[] sellDenyActions; + protected Action[] sellAllowActions; + protected Action[] sellLimitActions; + protected Action[] sellAllDenyActions; + protected Action[] sellAllAllowActions; + protected Action[] sellAllLimitActions; + + private SchedulerTask resetEarningsTask; + private int cachedDate; + + private boolean allowBundle; + private boolean allowShulkerBox; + + public BukkitMarketManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + this.priceMap = new HashMap<>(); + this.decorativeIcons = new HashMap<>(); + this.marketGUICache = new ConcurrentHashMap<>(); + this.cachedDate = getRealTimeDate(); + } + + @Override + public void load() { + this.loadConfig(); + Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap()); + this.resetEarningsTask = plugin.getScheduler().asyncRepeating(() -> { + int now = getRealTimeDate(); + if (this.cachedDate != now) { + this.cachedDate = now; + for (UserData userData : plugin.getStorageManager().getOnlineUsers()) { + userData.earningData().refresh(); + } + } + }, 1, 1, TimeUnit.SECONDS); + } + + private int getRealTimeDate() { + Calendar calendar = Calendar.getInstance(); + return (calendar.get(Calendar.MONTH) +1) * 100 + calendar.get(Calendar.DATE); + } + + @Override + public void unload() { + HandlerList.unregisterAll(this); + this.priceMap.clear(); + this.decorativeIcons.clear(); + if (this.resetEarningsTask != null) + this.resetEarningsTask.cancel(); + } + + private void loadConfig() { + Section config = BukkitConfigManager.getMainConfig().getSection("mechanics.market"); + + this.formula = config.getString("price-formula", "{base} + {bonus} * {size}"); + this.layout = config.getStringList("layout").toArray(new String[0]); + this.title = TextValue.auto(config.getString("title", "market.title")); + this.itemSlot = config.getString("item-slot.symbol", "I").charAt(0); + this.allowItemWithNoPrice = config.getBoolean("item-slot.allow-items-with-no-price", true); + this.allowBundle = config.getBoolean("allow-bundle", true); + this.allowShulkerBox = config.getBoolean("allow-shulker-box", true); + + Section sellAllSection = config.getSection("sell-all-icons"); + if (sellAllSection != null) { + this.sellAllSlot = sellAllSection.getString("symbol", "S").charAt(0); + this.sellFishingBag = sellAllSection.getBoolean("fishingbag", true); + + this.sellAllIconAllowItem = new GUIItemParser("allow", sellAllSection.getSection("allow-icon"), plugin.getConfigManager().getFormatFunctions()).getItem(); + this.sellAllIconDenyItem = new GUIItemParser("deny", sellAllSection.getSection("deny-icon"), plugin.getConfigManager().getFormatFunctions()).getItem(); + this.sellAllIconLimitItem = new GUIItemParser("limit", sellAllSection.getSection("limit-icon"), plugin.getConfigManager().getFormatFunctions()).getItem(); + + this.sellAllAllowActions = plugin.getActionManager().parseActions(sellAllSection.getSection("allow-icon.action")); + this.sellAllDenyActions = plugin.getActionManager().parseActions(sellAllSection.getSection("deny-icon.action")); + this.sellAllLimitActions = plugin.getActionManager().parseActions(sellAllSection.getSection("limit-icon.action")); + } + + Section sellSection = config.getSection("sell-icons"); + if (sellSection == null) { + // for old config compatibility + sellSection = config.getSection("functional-icons"); + } + if (sellSection != null) { + this.sellSlot = sellSection.getString("symbol", "B").charAt(0); + + this.sellIconAllowItem = new GUIItemParser("allow", sellSection.getSection("allow-icon"), plugin.getConfigManager().getFormatFunctions()).getItem(); + this.sellIconDenyItem = new GUIItemParser("deny", sellSection.getSection("deny-icon"), plugin.getConfigManager().getFormatFunctions()).getItem(); + this.sellIconLimitItem = new GUIItemParser("limit", sellSection.getSection("limit-icon"), plugin.getConfigManager().getFormatFunctions()).getItem(); + + this.sellAllowActions = plugin.getActionManager().parseActions(sellSection.getSection("allow-icon.action")); + this.sellDenyActions = plugin.getActionManager().parseActions(sellSection.getSection("deny-icon.action")); + this.sellLimitActions = plugin.getActionManager().parseActions(sellSection.getSection("limit-icon.action")); + } + + this.earningsLimit = config.getBoolean("limitation.enable", true) ? MathValue.auto(config.getString("limitation.earnings", "10000")) : MathValue.plain(-1); + + // Load item prices from the configuration + Section priceSection = config.getSection("item-price"); + if (priceSection != null) { + for (Map.Entry entry : priceSection.getStringRouteMappedValues(false).entrySet()) { + this.priceMap.put(entry.getKey(), MathValue.auto(entry.getValue())); + } + } + + // Load decorative icons from the configuration + Section decorativeSection = config.getSection("decorative-icons"); + if (decorativeSection != null) { + for (Map.Entry entry : decorativeSection.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + char symbol = Objects.requireNonNull(innerSection.getString("symbol")).charAt(0); + decorativeIcons.put(symbol, new GUIItemParser("gui", innerSection, plugin.getConfigManager().getFormatFunctions()).getItem()); + } + } + } + } + + /** + * Open the market GUI for a player + * + * @param player player + */ + @Override + public boolean openMarketGUI(Player player) { + Optional optionalUserData = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); + if (optionalUserData.isEmpty()) { + plugin.getPluginLogger().warn("Player " + player.getName() + "'s market data has not been loaded yet."); + return false; + } + Context context = Context.player(player); + MarketGUI gui = new MarketGUI(this, context, optionalUserData.get().earningData()); + gui.addElement(new MarketGUIElement(itemSlot, new ItemStack(Material.AIR))); + gui.addElement(new MarketDynamicGUIElement(sellSlot, new ItemStack(Material.AIR))); + gui.addElement(new MarketDynamicGUIElement(sellAllSlot, new ItemStack(Material.AIR))); + for (Map.Entry entry : decorativeIcons.entrySet()) { + gui.addElement(new MarketGUIElement(entry.getKey(), entry.getValue().build(context))); + } + gui.build().refresh().show(); + marketGUICache.put(player.getUniqueId(), gui); + return true; + } + + /** + * 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)) + return; + if (!(event.getInventory().getHolder() instanceof MarketGUIHolder)) + return; + MarketGUI gui = marketGUICache.remove(player.getUniqueId()); + if (gui != null) + 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 = marketGUICache.remove(event.getPlayer().getUniqueId()); + if (gui != null) + 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()) + return; + Inventory inventory = event.getInventory(); + if (!(inventory.getHolder() instanceof MarketGUIHolder)) + return; + Player player = (Player) event.getWhoClicked(); + MarketGUI gui = marketGUICache.get(player.getUniqueId()); + if (gui == null) { + event.setCancelled(true); + player.closeInventory(); + return; + } + + MarketGUIElement element = gui.getElement(itemSlot); + if (element == null) { + event.setCancelled(true); + return; + } + + List slots = element.getSlots(); + for (int dragSlot : event.getRawSlots()) { + if (!slots.contains(dragSlot)) { + event.setCancelled(true); + return; + } + } + + plugin.getScheduler().sync().runLater(gui::refresh, 1, player.getLocation()); + } + + /** + * This method handles inventory click events. + * + * @param event The InventoryClickEvent that triggered this method. + */ + @EventHandler (ignoreCancelled = true) + public void onClickInv(InventoryClickEvent event) { + 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; + + MarketGUI gui = marketGUICache.get(player.getUniqueId()); + if (gui == null) { + event.setCancelled(true); + player.closeInventory(); + return; + } + + EarningData earningData = gui.earningData; + earningData.refresh(); + double earningLimit = earningLimit(gui.context); + + if (clickedInv != player.getInventory()) { + int slot = event.getSlot(); + MarketGUIElement element = gui.getElement(slot); + if (element == null) { + event.setCancelled(true); + return; + } + + if (element.getSymbol() == itemSlot) { + if (!allowItemWithNoPrice) { + if (event.getAction() == InventoryAction.HOTBAR_MOVE_AND_READD || event.getAction() == InventoryAction.HOTBAR_SWAP) { + ItemStack moved = player.getInventory().getItem(event.getHotbarButton()); + double price = getItemPrice(gui.context, moved); + if (price <= 0) { + event.setCancelled(true); + return; + } + } + } + } else { + event.setCancelled(true); + } + + if (element.getSymbol() == sellSlot) { + + Pair pair = getItemsToSell(gui.context, gui.getItemsInGUI()); + double totalWorth = pair.right(); + gui.context.arg(ContextKeys.MONEY, money(totalWorth)) + .arg(ContextKeys.MONEY_FORMATTED, String.format("%.2f", totalWorth)) + .arg(ContextKeys.REST, money(earningLimit - earningData.earnings)) + .arg(ContextKeys.REST_FORMATTED, String.format("%.2f", (earningLimit - earningData.earnings))) + .arg(ContextKeys.SOLD_ITEM_AMOUNT, pair.left()); + + if (totalWorth > 0) { + if (earningLimit != -1 && (earningLimit - earningData.earnings) < totalWorth) { + // Can't earn more money + ActionManager.trigger(gui.context, sellLimitActions); + } else { + // Clear items and update earnings + clearWorthyItems(gui.context, gui.getItemsInGUI()); + earningData.earnings += totalWorth; + gui.context.arg(ContextKeys.REST, money(earningLimit - earningData.earnings)); + gui.context.arg(ContextKeys.REST_FORMATTED, String.format("%.2f", (earningLimit - earningData.earnings))); + ActionManager.trigger(gui.context, sellAllowActions); + } + } else { + // Nothing to sell + ActionManager.trigger(gui.context, sellDenyActions); + } + } else if (element.getSymbol() == sellAllSlot) { + ArrayList itemStacksToSell = new ArrayList<>(List.of(gui.context.getHolder().getInventory().getStorageContents())); + if (sellFishingBag) { + Optional optionalUserData = BukkitCustomFishingPlugin.getInstance().getStorageManager().getOnlineUser(gui.context.getHolder().getUniqueId()); + optionalUserData.ifPresent(userData -> itemStacksToSell.addAll(List.of(userData.holder().getInventory().getStorageContents()))); + } + Pair pair = getItemsToSell(gui.context, itemStacksToSell); + double totalWorth = pair.right(); + gui.context.arg(ContextKeys.MONEY, money(totalWorth)) + .arg(ContextKeys.MONEY_FORMATTED, String.format("%.2f", totalWorth)) + .arg(ContextKeys.REST, money(earningLimit - earningData.earnings)) + .arg(ContextKeys.REST_FORMATTED, String.format("%.2f", (earningLimit - earningData.earnings))) + .arg(ContextKeys.SOLD_ITEM_AMOUNT, pair.left()); + + if (totalWorth > 0) { + if (earningLimit != -1 && (earningLimit - earningData.earnings) < totalWorth) { + // Can't earn more money + ActionManager.trigger(gui.context, sellAllLimitActions); + } else { + // Clear items and update earnings + clearWorthyItems(gui.context, itemStacksToSell); + earningData.earnings += totalWorth; + gui.context.arg(ContextKeys.REST, money(earningLimit - earningData.earnings)); + gui.context.arg(ContextKeys.REST_FORMATTED, String.format("%.2f", (earningLimit - earningData.earnings))); + ActionManager.trigger(gui.context, sellAllAllowActions); + } + } else { + // Nothing to sell + ActionManager.trigger(gui.context, sellAllDenyActions); + } + } + } else { + // Handle interactions with the player's inventory + ItemStack current = event.getCurrentItem(); + if (!allowItemWithNoPrice) { + double price = getItemPrice(gui.context, current); + if (price <= 0) { + event.setCancelled(true); + return; + } + } + + if ((event.getClick() == ClickType.SHIFT_LEFT || event.getClick() == ClickType.SHIFT_RIGHT) + && (current != null && current.getType() != Material.AIR)) { + event.setCancelled(true); + MarketGUIElement element = gui.getElement(itemSlot); + if (element == null) return; + for (int slot : element.getSlots()) { + ItemStack itemStack = gui.inventory.getItem(slot); + if (itemStack != null && itemStack.getType() != Material.AIR) { + if (current.getType() == itemStack.getType() + && itemStack.getAmount() != itemStack.getType().getMaxStackSize() + && current.getItemMeta().equals(itemStack.getItemMeta()) + ) { + int left = itemStack.getType().getMaxStackSize() - itemStack.getAmount(); + if (current.getAmount() <= left) { + itemStack.setAmount(itemStack.getAmount() + current.getAmount()); + current.setAmount(0); + break; + } else { + current.setAmount(current.getAmount() - left); + itemStack.setAmount(itemStack.getType().getMaxStackSize()); + } + } + } else { + gui.inventory.setItem(slot, current.clone()); + current.setAmount(0); + break; + } + } + } + } + + // Refresh the GUI + plugin.getScheduler().sync().runLater(gui::refresh, 1, player.getLocation()); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public double getItemPrice(Context context, ItemStack itemStack) { + if (itemStack == null || itemStack.getType() == Material.AIR) + return 0; + + Item wrapped = ((BukkitItemFactory) plugin.getItemManager().getFactory()).wrap(itemStack); + double price = (double) wrapped.getTag("Price").orElse(0d); + if (price != 0) { + // If a custom price is defined in the ItemStack's NBT data, use it. + return price * itemStack.getAmount(); + } + + if (allowBundle && itemStack.getItemMeta() instanceof BundleMeta bundleMeta) { + Pair pair = getItemsToSell(context, bundleMeta.getItems()); + return pair.right(); + } + + if (allowShulkerBox && itemStack.getItemMeta() instanceof BlockStateMeta stateMeta) { + if (stateMeta.getBlockState() instanceof ShulkerBox shulkerBox) { + Pair pair = getItemsToSell(context, Arrays.stream(shulkerBox.getInventory().getStorageContents()).filter(Objects::nonNull).toList()); + return pair.right(); + } + } + + // If no custom price is defined, attempt to fetch the price from a predefined price map. + String itemID = itemStack.getType().name(); + Optional optionalCMD = wrapped.customModelData(); + if (optionalCMD.isPresent()) { + itemID = itemID + ":" + optionalCMD.get(); + } + + MathValue formula = priceMap.get(itemID); + if (formula == null) return 0; + + return formula.evaluate(context) * itemStack.getAmount(); + } + + @Override + public String getFormula() { + return formula; + } + + @Override + public double earningLimit(Context context) { + return earningsLimit.evaluate(context); + } + + public Pair getItemsToSell(Context context, List itemStacks) { + int amount = 0; + double worth = 0d; + for (ItemStack itemStack : itemStacks) { + double price = getItemPrice(context, itemStack); + if (price > 0 && itemStack != null) { + amount += itemStack.getAmount(); + worth += price; + } + } + return Pair.of(amount, worth); + } + + @SuppressWarnings("UnstableApiUsage") + public void clearWorthyItems(Context context, List itemStacks) { + for (ItemStack itemStack : itemStacks) { + double price = getItemPrice(context, itemStack); + if (price > 0 && itemStack != null) { + if (allowBundle && itemStack.getItemMeta() instanceof BundleMeta bundleMeta) { + clearWorthyItems(context, bundleMeta.getItems()); + itemStack.setItemMeta(bundleMeta); + continue; + } + if (allowShulkerBox && itemStack.getItemMeta() instanceof BlockStateMeta stateMeta) { + if (stateMeta.getBlockState() instanceof ShulkerBox shulkerBox) { + clearWorthyItems(context, Arrays.stream(shulkerBox.getInventory().getStorageContents()).filter(Objects::nonNull).toList()); + stateMeta.setBlockState(shulkerBox); + itemStack.setItemMeta(stateMeta); + continue; + } + } + itemStack.setAmount(0); + } + } + } + + protected String money(double money) { + String str = String.format("%.2f", money); + return str.replace(",", "."); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketDynamicGUIElement.java b/core/src/main/java/net/momirealms/customfishing/bukkit/market/MarketDynamicGUIElement.java similarity index 94% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketDynamicGUIElement.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/market/MarketDynamicGUIElement.java index f64e7556..0b3e4236 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketDynamicGUIElement.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/market/MarketDynamicGUIElement.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.market; +package net.momirealms.customfishing.bukkit.market; import org.bukkit.inventory.ItemStack; diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/market/MarketGUI.java b/core/src/main/java/net/momirealms/customfishing/bukkit/market/MarketGUI.java new file mode 100644 index 00000000..3b103c47 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/market/MarketGUI.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.market; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.market.MarketGUIHolder; +import net.momirealms.customfishing.api.storage.data.EarningData; +import net.momirealms.customfishing.api.storage.user.UserData; +import net.momirealms.customfishing.bukkit.util.PlayerUtils; +import net.momirealms.customfishing.common.helper.AdventureHelper; +import net.momirealms.customfishing.common.util.Pair; +import net.momirealms.sparrow.heart.SparrowHeart; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +@SuppressWarnings("DuplicatedCode") +public class MarketGUI { + + private final HashMap itemsCharMap; + private final HashMap itemsSlotMap; + private final BukkitMarketManager manager; + protected final Inventory inventory; + protected final Context context; + protected final EarningData earningData; + + public MarketGUI(BukkitMarketManager manager, Context context, EarningData earningData) { + this.manager = manager; + this.context = context; + this.earningData = earningData; + this.itemsCharMap = new HashMap<>(); + this.itemsSlotMap = new HashMap<>(); + var holder = new MarketGUIHolder(); + this.inventory = Bukkit.createInventory(holder, manager.layout.length * 9); + holder.setInventory(this.inventory); + } + + private void init() { + int line = 0; + for (String content : manager.layout) { + for (int index = 0; index < 9; index++) { + char symbol; + if (index < content.length()) symbol = content.charAt(index); + else symbol = ' '; + MarketGUIElement element = itemsCharMap.get(symbol); + if (element != null) { + element.addSlot(index + line * 9); + itemsSlotMap.put(index + line * 9, element); + } + } + line++; + } + for (Map.Entry entry : itemsSlotMap.entrySet()) { + this.inventory.setItem(entry.getKey(), entry.getValue().getItemStack().clone()); + } + } + + @SuppressWarnings("UnusedReturnValue") + public MarketGUI addElement(MarketGUIElement... elements) { + for (MarketGUIElement element : elements) { + itemsCharMap.put(element.getSymbol(), element); + } + return this; + } + + public MarketGUI build() { + init(); + return this; + } + + public void show() { + context.getHolder().openInventory(inventory); + SparrowHeart.getInstance().updateInventoryTitle(context.getHolder(), AdventureHelper.componentToJson(AdventureHelper.miniMessage(manager.title.render(context)))); + } + + @Nullable + public MarketGUIElement getElement(int slot) { + return itemsSlotMap.get(slot); + } + + @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 earningLimit = manager.earningLimit(context); + MarketDynamicGUIElement sellElement = (MarketDynamicGUIElement) getElement(manager.sellSlot); + if (sellElement != null && !sellElement.getSlots().isEmpty()) { + Pair pair = manager.getItemsToSell(context, getItemsInGUI()); + double totalWorth = pair.right(); + int soldAmount = pair.left(); + context.arg(ContextKeys.MONEY, manager.money(totalWorth)) + .arg(ContextKeys.MONEY_FORMATTED, String.format("%.2f", totalWorth)) + .arg(ContextKeys.REST, manager.money(earningLimit - earningData.earnings)) + .arg(ContextKeys.REST_FORMATTED, String.format("%.2f", (earningLimit - earningData.earnings))) + .arg(ContextKeys.SOLD_ITEM_AMOUNT, soldAmount); + if (totalWorth <= 0) { + sellElement.setItemStack(manager.sellIconDenyItem.build(context)); + } else if (earningLimit != -1 && (earningLimit - earningData.earnings < totalWorth)) { + sellElement.setItemStack(manager.sellIconLimitItem.build(context)); + } else { + sellElement.setItemStack(manager.sellIconAllowItem.build(context)); + } + } + + MarketDynamicGUIElement sellAllElement = (MarketDynamicGUIElement) getElement(manager.sellAllSlot); + if (sellAllElement != null && !sellAllElement.getSlots().isEmpty()) { + ArrayList itemStacksToSell = new ArrayList<>(List.of(context.getHolder().getInventory().getStorageContents())); + if (manager.sellFishingBag) { + Optional optionalUserData = BukkitCustomFishingPlugin.getInstance().getStorageManager().getOnlineUser(context.getHolder().getUniqueId()); + optionalUserData.ifPresent(userData -> itemStacksToSell.addAll(List.of(userData.holder().getInventory().getStorageContents()))); + } + Pair pair = manager.getItemsToSell(context, itemStacksToSell); + double totalWorth = pair.right(); + int soldAmount = pair.left(); + context.arg(ContextKeys.MONEY, manager.money(totalWorth)) + .arg(ContextKeys.MONEY_FORMATTED, String.format("%.2f", totalWorth)) + .arg(ContextKeys.REST, manager.money(earningLimit - earningData.earnings)) + .arg(ContextKeys.REST_FORMATTED, String.format("%.2f", (earningLimit - earningData.earnings))) + .arg(ContextKeys.SOLD_ITEM_AMOUNT, soldAmount); + if (totalWorth <= 0) { + sellAllElement.setItemStack(manager.sellAllIconAllowItem.build(context)); + } else if (earningLimit != -1 && (earningLimit - earningData.earnings < totalWorth)) { + sellAllElement.setItemStack(manager.sellAllIconLimitItem.build(context)); + } else { + sellAllElement.setItemStack(manager.sellAllIconAllowItem.build(context)); + } + } + + for (Map.Entry entry : itemsSlotMap.entrySet()) { + if (entry.getValue() instanceof MarketDynamicGUIElement dynamicGUIElement) { + this.inventory.setItem(entry.getKey(), dynamicGUIElement.getItemStack().clone()); + } + } + return this; + } + + public List getItemsInGUI() { + MarketGUIElement itemElement = getElement(manager.itemSlot); + if (itemElement == null) return List.of(); + return itemElement.getSlots().stream().map(inventory::getItem).filter(Objects::nonNull).toList(); + } + + public int getEmptyItemSlot() { + MarketGUIElement itemElement = getElement(manager.itemSlot); + if (itemElement == null) { + return -1; + } + for (int slot : itemElement.getSlots()) { + ItemStack itemStack = inventory.getItem(slot); + if (itemStack == null || itemStack.getType() == Material.AIR) { + return slot; + } + } + return -1; + } + + public void returnItems() { + MarketGUIElement itemElement = getElement(manager.itemSlot); + if (itemElement == null) { + return; + } + for (int slot : itemElement.getSlots()) { + ItemStack itemStack = inventory.getItem(slot); + if (itemStack != null && itemStack.getType() != Material.AIR) { + PlayerUtils.giveItem(context.getHolder(), itemStack, itemStack.getAmount()); + inventory.setItem(slot, new ItemStack(Material.AIR)); + } + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUIElement.java b/core/src/main/java/net/momirealms/customfishing/bukkit/market/MarketGUIElement.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUIElement.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/market/MarketGUIElement.java index 774b12cb..6242d211 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUIElement.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/market/MarketGUIElement.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.market; +package net.momirealms.customfishing.bukkit.market; import org.bukkit.inventory.ItemStack; diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/requirement/BukkitRequirementManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/requirement/BukkitRequirementManager.java new file mode 100644 index 00000000..dff85941 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/requirement/BukkitRequirementManager.java @@ -0,0 +1,1210 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.requirement; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.integration.LevelerProvider; +import net.momirealms.customfishing.api.integration.SeasonProvider; +import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.action.ActionManager; +import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.effect.EffectProperties; +import net.momirealms.customfishing.api.mechanic.loot.Loot; +import net.momirealms.customfishing.api.mechanic.misc.season.Season; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import net.momirealms.customfishing.api.mechanic.misc.value.TextValue; +import net.momirealms.customfishing.api.mechanic.requirement.*; +import net.momirealms.customfishing.api.util.MoonPhase; +import net.momirealms.customfishing.bukkit.integration.VaultHook; +import net.momirealms.customfishing.common.util.ClassUtils; +import net.momirealms.customfishing.common.util.ListUtils; +import net.momirealms.customfishing.common.util.Pair; +import net.momirealms.sparrow.heart.SparrowHeart; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +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; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.*; + +import static java.util.Objects.requireNonNull; + +public class BukkitRequirementManager implements RequirementManager { + + private final BukkitCustomFishingPlugin plugin; + private final HashMap> requirementFactoryMap = new HashMap<>(); + private static final String EXPANSION_FOLDER = "expansions/requirement"; + + public BukkitRequirementManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + this.registerBuiltInRequirements(); + } + + @Override + public void reload() { + this.loadExpansions(); + } + + @Override + public void disable() { + this.requirementFactoryMap.clear(); + } + + @Override + public boolean registerRequirement(@NotNull String type, @NotNull RequirementFactory requirementFactory) { + if (this.requirementFactoryMap.containsKey(type)) return false; + this.requirementFactoryMap.put(type, requirementFactory); + return true; + } + + @Override + public boolean unregisterRequirement(@NotNull String type) { + return this.requirementFactoryMap.remove(type) != null; + } + + @Nullable + @Override + public RequirementFactory getRequirementFactory(@NotNull String type) { + return requirementFactoryMap.get(type); + } + + @Override + public boolean hasRequirement(@NotNull String type) { + return requirementFactoryMap.containsKey(type); + } + + @NotNull + @Override + @SuppressWarnings("unchecked") + public Requirement[] parseRequirements(Section section, boolean runActions) { + List> requirements = new ArrayList<>(); + if (section != null) + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String typeOrName = entry.getKey(); + if (hasRequirement(typeOrName)) { + requirements.add(parseRequirement(typeOrName, entry.getValue())); + } else { + requirements.add(parseRequirement(section.getSection(typeOrName), runActions)); + } + } + return requirements.toArray(new Requirement[0]); + } + + @NotNull + @Override + public Requirement parseRequirement(@NotNull Section section, boolean runActions) { + List> actionList = new ArrayList<>(); + if (runActions && section.contains("not-met-actions")) { + actionList.addAll(List.of(plugin.getActionManager().parseActions(requireNonNull(section.getSection("not-met-actions"))))); + } + String type = section.getString("type"); + if (type == null) { + plugin.getPluginLogger().warn("No requirement type found at " + section.getRouteAsString()); + return EmptyRequirement.INSTANCE; + } + var factory = getRequirementFactory(type); + if (factory == null) { + plugin.getPluginLogger().warn("Requirement type: " + type + " not exists"); + return EmptyRequirement.INSTANCE; + } + return factory.process(section.get("value"), actionList, runActions); + } + + @NotNull + @Override + public Requirement parseRequirement(@NotNull String type, @NotNull Object value) { + RequirementFactory factory = getRequirementFactory(type); + if (factory == null) { + plugin.getPluginLogger().warn("Requirement type: " + type + " doesn't exist."); + return EmptyRequirement.INSTANCE; + } + return factory.process(value); + } + + private void registerBuiltInRequirements() { + this.registerTimeRequirement(); + this.registerYRequirement(); + this.registerInWaterRequirement(); + this.registerInVoidRequirement(); + this.registerInLavaRequirement(); + this.registerAndRequirement(); + this.registerOrRequirement(); + this.registerGroupRequirement(); + this.registerRodRequirement(); + this.registerPAPIRequirement(); + this.registerSeasonRequirement(); + this.registerPermissionRequirement(); + this.registerMoonPhaseRequirement(); + this.registerCoolDownRequirement(); + this.registerDateRequirement(); + this.registerWeatherRequirement(); + this.registerBiomeRequirement(); + this.registerWorldRequirement(); + this.registerMoneyRequirement(); + this.registerLevelRequirement(); + this.registerRandomRequirement(); + this.registerIceFishingRequirement(); + this.registerOpenWaterRequirement(); + this.registerBaitRequirement(); + this.registerLootRequirement(); + this.registerSizeRequirement(); + this.registerLootTypeRequirement(); + this.registerHasStatsRequirement(); + this.registerHookRequirement(); + this.registerEnvironmentRequirement(); + this.registerListRequirement(); + this.registerInBagRequirement(); + this.registerCompetitionRequirement(); + this.registerPluginLevelRequirement(); + this.registerItemInHandRequirement(); + this.registerImpossibleRequirement(); + } + + private void registerImpossibleRequirement() { + registerRequirement("impossible", ((args, actions, runActions) -> context -> { + if (runActions) ActionManager.trigger(context, actions); + return false; + })); + } + + private void registerCompetitionRequirement() { + registerRequirement("competition", (args, actions, runActions) -> { + if (args instanceof Section section) { + boolean onCompetition = section.getBoolean("ongoing", true); + List ids = ListUtils.toList(section.get("id")); + return context -> { + if (ids.isEmpty()) { + if (plugin.getCompetitionManager().getOnGoingCompetition() != null == onCompetition) { + return true; + } + } else { + FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); + if (onCompetition) { + if (competition != null) + if (ids.contains(competition.getConfig().key())) + return true; + } else { + if (competition == null) + return true; + } + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at competition requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + } + + private void registerInBagRequirement() { + registerRequirement("in-fishingbag", (args, actions, runActions) -> { + boolean arg = (boolean) args; + return context -> { + boolean inBag = Optional.ofNullable(context.arg(ContextKeys.IN_BAG)).orElse(false); + if (inBag == arg) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerItemInHandRequirement() { + registerRequirement("item-in-hand", (args, actions, runActions) -> { + if (args instanceof Section section) { + boolean mainOrOff = section.getString("hand","main").equalsIgnoreCase("main"); + int amount = section.getInt("amount", 1); + List items = ListUtils.toList(section.get("item")); + return context -> { + ItemStack itemStack = mainOrOff ? + context.getHolder().getInventory().getItemInMainHand() + : context.getHolder().getInventory().getItemInOffHand(); + String id = plugin.getItemManager().getItemID(itemStack); + if (items.contains(id) && itemStack.getAmount() >= amount) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at item-in-hand requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + } + + private void registerPluginLevelRequirement() { + registerRequirement("plugin-level", (args, actions, runActions) -> { + if (args instanceof Section section) { + String pluginName = section.getString("plugin"); + int level = section.getInt("level"); + String target = section.getString("target"); + return context -> { + LevelerProvider levelerProvider = plugin.getIntegrationManager().getLevelerProvider(pluginName); + if (levelerProvider == null) { + plugin.getPluginLogger().warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation."); + return true; + } + if (levelerProvider.getLevel(context.getHolder(), target) >= level) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at plugin-level requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + } + + private void registerTimeRequirement() { + registerRequirement("time", (args, actions, runActions) -> { + List list = ListUtils.toList(args); + List> timePairs = list.stream().map(line -> { + String[] split = line.split("~"); + return new Pair<>(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + }).toList(); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + long time = location.getWorld().getTime(); + for (Pair pair : timePairs) + if (time >= pair.left() && time <= pair.right()) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerYRequirement() { + registerRequirement("ypos", (args, actions, runActions) -> { + List list = ListUtils.toList(args); + List> posPairs = list.stream().map(line -> { + String[] split = line.split("~"); + return new Pair<>(Double.parseDouble(split[0]), Double.parseDouble(split[1])); + }).toList(); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + double y = location.getY(); + for (Pair pair : posPairs) + if (y >= pair.left() && y <= pair.right()) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerOrRequirement() { + registerRequirement("||", (args, actions, runActions) -> { + if (args instanceof Section section) { + Requirement[] requirements = parseRequirements(section, runActions); + return context -> { + for (Requirement requirement : requirements) + if (requirement.isSatisfied(context)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at || requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + } + + private void registerAndRequirement() { + registerRequirement("&&", (args, actions, runActions) -> { + if (args instanceof Section section) { + Requirement[] requirements = parseRequirements(section, runActions); + return context -> { + outer: { + for (Requirement requirement : requirements) + if (!requirement.isSatisfied(context)) + break outer; + return true; + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at && requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + } + + private void registerInWaterRequirement() { + registerRequirement("in-water", (args, actions, runActions) -> { + boolean inWater = (boolean) args; + return context -> { + boolean in_water = Optional.ofNullable(context.arg(ContextKeys.SURROUNDING)).orElse("").equals(EffectProperties.WATER_FISHING.key()); + if (in_water == inWater) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerInVoidRequirement() { + registerRequirement("in-void", (args, actions, runActions) -> { + boolean inVoid = (boolean) args; + return context -> { + boolean in_void = Optional.ofNullable(context.arg(ContextKeys.SURROUNDING)).orElse("").equals(EffectProperties.VOID_FISHING.key()); + if (in_void == inVoid) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerInLavaRequirement() { + // Deprecated requirement type + registerRequirement("lava-fishing", (args, actions, runActions) -> { + boolean inLava = (boolean) args; + if (!inLava) { + // in water + return context -> { + boolean in_water = Optional.ofNullable(context.arg(ContextKeys.SURROUNDING)).orElse("").equals(EffectProperties.WATER_FISHING.key()); + if (in_water) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } + // in lava + return context -> { + boolean in_lava = Optional.ofNullable(context.arg(ContextKeys.SURROUNDING)).orElse("").equals(EffectProperties.LAVA_FISHING.key()); + if (in_lava) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("in-lava", (args, actions, runActions) -> { + boolean inLava = (boolean) args; + return context -> { + boolean in_lava = Optional.ofNullable(context.arg(ContextKeys.SURROUNDING)).orElse("").equals(EffectProperties.LAVA_FISHING.key()); + if (in_lava == inLava) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerRodRequirement() { + registerRequirement("rod", (args, actions, runActions) -> { + HashSet rods = new HashSet<>(ListUtils.toList(args)); + return context -> { + String id = context.arg(ContextKeys.ROD); + if (rods.contains(id)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("!rod", (args, actions, runActions) -> { + HashSet rods = new HashSet<>(ListUtils.toList(args)); + return context -> { + String id = context.arg(ContextKeys.ROD); + if (!rods.contains(id)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerGroupRequirement() { + registerRequirement("group", (args, actions, runActions) -> { + HashSet groups = new HashSet<>(ListUtils.toList(args)); + return context -> { + String lootID = context.arg(ContextKeys.ID); + Optional loot = plugin.getLootManager().getLoot(lootID); + if (loot.isEmpty()) return false; + String[] group = loot.get().lootGroup(); + if (group != null) + for (String x : group) + if (groups.contains(x)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("!group", (args, actions, runActions) -> { + HashSet groups = new HashSet<>(ListUtils.toList(args)); + return context -> { + String lootID = context.arg(ContextKeys.ID); + Optional loot = plugin.getLootManager().getLoot(lootID); + if (loot.isEmpty()) return false; + String[] group = loot.get().lootGroup(); + if (group == null) + return true; + outer: { + for (String x : group) + if (groups.contains(x)) + break outer; + return true; + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerLootRequirement() { + registerRequirement("loot", (args, actions, runActions) -> { + HashSet arg = new HashSet<>(ListUtils.toList(args)); + return context -> { + String lootID = context.arg(ContextKeys.ID); + if (arg.contains(lootID)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("!loot", (args, actions, runActions) -> { + HashSet arg = new HashSet<>(ListUtils.toList(args)); + return context -> { + String lootID = context.arg(ContextKeys.ID); + if (!arg.contains(lootID)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerHookRequirement() { + registerRequirement("hook", (args, actions, runActions) -> { + HashSet hooks = new HashSet<>(ListUtils.toList(args)); + return context -> { + String id = context.arg(ContextKeys.HOOK); + if (hooks.contains(id)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("!hook", (args, actions, runActions) -> { + HashSet hooks = new HashSet<>(ListUtils.toList(args)); + return context -> { + String id = context.arg(ContextKeys.HOOK); + if (!hooks.contains(id)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("has-hook", (args, actions, runActions) -> { + boolean has = (boolean) args; + return context -> { + String id = context.arg(ContextKeys.HOOK); + if (id != null && has) return true; + if (id == null && !has) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerBaitRequirement() { + registerRequirement("bait", (args, actions, runActions) -> { + HashSet arg = new HashSet<>(ListUtils.toList(args)); + return context -> { + String id = context.arg(ContextKeys.BAIT); + if (arg.contains(id)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("!bait", (args, actions, runActions) -> { + HashSet arg = new HashSet<>(ListUtils.toList(args)); + return context -> { + String id = context.arg(ContextKeys.BAIT); + if (!arg.contains(id)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("has-bait", (args, actions, runActions) -> { + boolean has = (boolean) args; + return context -> { + String id = context.arg(ContextKeys.BAIT); + if (id != null && has) return true; + if (id == null && !has) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerSizeRequirement() { + registerRequirement("has-size", (args, actions, runActions) -> { + boolean has = (boolean) args; + return context -> { + float size = Optional.ofNullable(context.arg(ContextKeys.SIZE)).orElse(-1f); + if (size != -1 && has) return true; + if (size == -1 && !has) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerOpenWaterRequirement() { + registerRequirement("open-water", (args, actions, runActions) -> { + boolean openWater = (boolean) args; + return context -> { + boolean current = Optional.ofNullable(context.arg(ContextKeys.OPEN_WATER)).orElse(false); + if (openWater == current) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerHasStatsRequirement() { + registerRequirement("has-stats", (args, actions, runActions) -> { + boolean has = (boolean) args; + return context -> { + String loot = context.arg(ContextKeys.ID); + Optional lootInstance = plugin.getLootManager().getLoot(loot); + if (lootInstance.isPresent()) { + if (!lootInstance.get().disableStats() && has) return true; + if (lootInstance.get().disableStats() && !has) return true; + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerLootTypeRequirement() { + registerRequirement("loot-type", (args, actions, runActions) -> { + List types = ListUtils.toList(args); + return context -> { + String loot = context.arg(ContextKeys.ID); + Optional lootInstance = plugin.getLootManager().getLoot(loot); + if (lootInstance.isPresent()) { + if (types.contains(lootInstance.get().type().name().toLowerCase(Locale.ENGLISH))) + return true; + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("!loot-type", (args, actions, runActions) -> { + List types = ListUtils.toList(args); + return context -> { + String loot = context.arg(ContextKeys.ID); + Optional lootInstance = plugin.getLootManager().getLoot(loot); + if (lootInstance.isPresent()) { + if (!types.contains(lootInstance.get().type().name().toLowerCase(Locale.ENGLISH))) + return true; + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerListRequirement() { + registerRequirement("list", (args, actions, runActions) -> { + plugin.getPluginLogger().severe("It seems that you made a mistake where you put \"list\" into \"conditions\" section."); + plugin.getPluginLogger().warn("list:"); + for (String e : ListUtils.toList(args)) { + plugin.getPluginLogger().warn(" - " + e); + } + return EmptyRequirement.INSTANCE; + }); + } + + private void registerEnvironmentRequirement() { + registerRequirement("environment", (args, actions, runActions) -> { + List environments = ListUtils.toList(args); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + var name = location.getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); + if (environments.contains(name)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("!environment", (args, actions, runActions) -> { + List environments = ListUtils.toList(args); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + var name = location.getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); + if (!environments.contains(name)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerIceFishingRequirement() { + registerRequirement("ice-fishing", (args, actions, runActions) -> { + boolean iceFishing = (boolean) args; + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.OTHER_LOCATION)); + int water = 0, ice = 0; + for (int i = -2; i <= 2; i++) + for (int j = -1; j <= 2; j++) + for (int k = -2; k <= 2; k++) { + Block block = location.clone().add(i, j, k).getBlock(); + Material material = block.getType(); + switch (material) { + case ICE -> ice++; + case WATER -> water++; + } + } + if ((ice >= 16 && water >= 25) == iceFishing) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerLevelRequirement() { + registerRequirement("level", (args, actions, runActions) -> { + MathValue value = MathValue.auto(args); + return context -> { + int current = context.getHolder().getLevel(); + if (current >= value.evaluate(context)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerMoneyRequirement() { + registerRequirement("money", (args, actions, runActions) -> { + MathValue value = MathValue.auto(args); + return context -> { + double current = VaultHook.getBalance(context.getHolder()); + if (current >= value.evaluate(context)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerRandomRequirement() { + registerRequirement("random", (args, actions, runActions) -> { + MathValue value = MathValue.auto(args); + return context -> { + if (Math.random() < value.evaluate(context)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerBiomeRequirement() { + registerRequirement("biome", (args, actions, runActions) -> { + HashSet biomes = new HashSet<>(ListUtils.toList(args)); + return context -> { + Location location = requireNonNull(Optional.ofNullable(context.arg(ContextKeys.OTHER_LOCATION)).orElse(context.arg(ContextKeys.LOCATION))); + String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(location); + if (biomes.contains(currentBiome)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("!biome", (args, actions, runActions) -> { + HashSet biomes = new HashSet<>(ListUtils.toList(args)); + return context -> { + Location location = requireNonNull(Optional.ofNullable(context.arg(ContextKeys.OTHER_LOCATION)).orElse(context.arg(ContextKeys.LOCATION))); + String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(location); + if (!biomes.contains(currentBiome)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerMoonPhaseRequirement() { + registerRequirement("moon-phase", (args, actions, runActions) -> { + HashSet moonPhases = new HashSet<>(ListUtils.toList(args)); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + long days = location.getWorld().getFullTime() / 24_000; + if (moonPhases.contains(MoonPhase.getPhase(days).name().toLowerCase(Locale.ENGLISH))) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("!moon-phase", (args, actions, runActions) -> { + HashSet moonPhases = new HashSet<>(ListUtils.toList(args)); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + long days = location.getWorld().getFullTime() / 24_000; + if (!moonPhases.contains(MoonPhase.getPhase(days).name().toLowerCase(Locale.ENGLISH))) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerWorldRequirement() { + registerRequirement("world", (args, actions, runActions) -> { + HashSet worlds = new HashSet<>(ListUtils.toList(args)); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + if (worlds.contains(location.getWorld().getName())) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("!world", (args, actions, runActions) -> { + HashSet worlds = new HashSet<>(ListUtils.toList(args)); + return context -> { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + if (!worlds.contains(location.getWorld().getName())) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerWeatherRequirement() { + registerRequirement("weather", (args, actions, runActions) -> { + HashSet weathers = new HashSet<>(ListUtils.toList(args)); + return context -> { + String currentWeather; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + World world = location.getWorld(); + if (world.isClearWeather()) currentWeather = "clear"; + else if (world.isThundering()) currentWeather = "thunder"; + else currentWeather = "rain"; + if (weathers.contains(currentWeather)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerCoolDownRequirement() { + registerRequirement("cooldown", (args, actions, runActions) -> { + if (args instanceof Section section) { + String key = section.getString("key"); + int time = section.getInt("time"); + return context -> { + if (!plugin.getCoolDownManager().isCoolDown(context.getHolder().getUniqueId(), key, time)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at cooldown requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + } + + private void registerDateRequirement() { + registerRequirement("date", (args, actions, runActions) -> { + HashSet dates = new HashSet<>(ListUtils.toList(args)); + return context -> { + Calendar calendar = Calendar.getInstance(); + String current = (calendar.get(Calendar.MONTH) + 1) + "/" + calendar.get(Calendar.DATE); + if (dates.contains(current)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerPermissionRequirement() { + registerRequirement("permission", (args, actions, runActions) -> { + List perms = ListUtils.toList(args); + return context -> { + for (String perm : perms) + if (context.getHolder().hasPermission(perm)) + return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + registerRequirement("!permission", (args, actions, runActions) -> { + List perms = ListUtils.toList(args); + return context -> { + for (String perm : perms) + if (context.getHolder().hasPermission(perm)) { + if (runActions) ActionManager.trigger(context, actions); + return false; + } + return true; + }; + }); + } + + private void registerSeasonRequirement() { + registerRequirement("season", (args, actions, runActions) -> { + List seasons = ListUtils.toList(args); + return context -> { + SeasonProvider seasonProvider = plugin.getIntegrationManager().getSeasonProvider(); + if (seasonProvider == null) return true; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + World world = location.getWorld(); + Season season = seasonProvider.getSeason(world); + if (seasons.contains(season.name().toLowerCase(Locale.ENGLISH))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + private void registerPAPIRequirement() { + registerRequirement("<", (args, actions, runActions) -> { + if (args instanceof Section section) { + MathValue v1 = MathValue.auto(section.get("value1")); + MathValue v2 = MathValue.auto(section.get("value2")); + return context -> { + if (v1.evaluate(context) < v2.evaluate(context)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at < requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("<=", (args, actions, runActions) -> { + if (args instanceof Section section) { + MathValue v1 = MathValue.auto(section.get("value1")); + MathValue v2 = MathValue.auto(section.get("value2")); + return context -> { + if (v1.evaluate(context) <= v2.evaluate(context)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at <= requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("!=", (args, actions, runActions) -> { + if (args instanceof Section section) { + MathValue v1 = MathValue.auto(section.get("value1")); + MathValue v2 = MathValue.auto(section.get("value2")); + return context -> { + if (v1.evaluate(context) != v2.evaluate(context)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at != requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("==", (args, actions, runActions) -> { + if (args instanceof Section section) { + MathValue v1 = MathValue.auto(section.get("value1")); + MathValue v2 = MathValue.auto(section.get("value2")); + return context -> { + if (v1.evaluate(context) == v2.evaluate(context)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at == requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement(">=", (args, actions, runActions) -> { + if (args instanceof Section section) { + MathValue v1 = MathValue.auto(section.get("value1")); + MathValue v2 = MathValue.auto(section.get("value2")); + return context -> { + if (v1.evaluate(context) >= v2.evaluate(context)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at >= requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement(">", (args, actions, runActions) -> { + if (args instanceof Section section) { + MathValue v1 = MathValue.auto(section.get("value1")); + MathValue v2 = MathValue.auto(section.get("value2")); + return context -> { + if (v1.evaluate(context) > v2.evaluate(context)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at > requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("regex", (args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("papi", "")); + String v2 = section.getString("regex", ""); + return context -> { + if (v1.render(context).matches(v2)) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at regex requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("startsWith", (args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (v1.render(context).startsWith(v2.render(context))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at startsWith requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("!startsWith", (args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (!v1.render(context).startsWith(v2.render(context))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at !startsWith requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("endsWith", (args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (v1.render(context).endsWith(v2.render(context))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at endsWith requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("!endsWith", (args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (!v1.render(context).endsWith(v2.render(context))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at !endsWith requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("contains", (args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (v1.render(context).contains(v2.render(context))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at contains requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("!contains", (args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (!v1.render(context).contains(v2.render(context))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at !contains requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("in-list", (args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue papi = TextValue.auto(section.getString("papi", "")); + List values = ListUtils.toList(section.get("values")); + return context -> { + if (values.contains(papi.render(context))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at in-list requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("!in-list", (args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue papi = TextValue.auto(section.getString("papi", "")); + List values = ListUtils.toList(section.get("values")); + return context -> { + if (!values.contains(papi.render(context))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at !in-list requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("equals", (args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (v1.render(context).equals(v2.render(context))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at equals requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + registerRequirement("!equals", (args, actions, runActions) -> { + if (args instanceof Section section) { + TextValue v1 = TextValue.auto(section.getString("value1", "")); + TextValue v2 = TextValue.auto(section.getString("value2", "")); + return context -> { + if (!v1.render(context).equals(v2.render(context))) return true; + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at !equals requirement which is expected be `Section`"); + return EmptyRequirement.INSTANCE; + } + }); + } + + private void registerPotionEffectRequirement() { + registerRequirement("potion-effect", (args, actions, runActions) -> { + String potions = (String) args; + String[] split = potions.split("(<=|>=|<|>|==)", 2); + PotionEffectType type = PotionEffectType.getByName(split[0]); + if (type == null) { + plugin.getPluginLogger().warn("Potion effect doesn't exist: " + split[0]); + return EmptyRequirement.INSTANCE; + } + int required = Integer.parseInt(split[1]); + String operator = potions.substring(split[0].length(), potions.length() - split[1].length()); + return context -> { + int level = -1; + PotionEffect potionEffect = context.getHolder().getPotionEffect(type); + if (potionEffect != null) { + level = potionEffect.getAmplifier(); + } + boolean result = false; + switch (operator) { + case ">=" -> { + if (level >= required) result = true; + } + case ">" -> { + if (level > required) result = true; + } + case "==" -> { + if (level == required) result = true; + } + case "!=" -> { + if (level != required) result = true; + } + case "<=" -> { + if (level <= required) result = true; + } + case "<" -> { + if (level < required) result = true; + } + } + if (result) { + return true; + } + if (runActions) ActionManager.trigger(context, actions); + return false; + }; + }); + } + + /** + * 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. + */ + @SuppressWarnings({"ResultOfMethodCallIgnored", "unchecked"}) + 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 = (Class>) ClassUtils.findClass(expansionJar, RequirementExpansion.class); + classes.add(expansionClass); + } catch (IOException | ClassNotFoundException e) { + plugin.getPluginLogger().warn("Failed to load expansion: " + expansionJar.getName(), e); + } + } + } + try { + for (Class> expansionClass : classes) { + RequirementExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); + unregisterRequirement(expansion.getRequirementType()); + registerRequirement(expansion.getRequirementType(), expansion.getRequirementFactory()); + plugin.getPluginLogger().info("Loaded requirement expansion: " + expansion.getRequirementType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor()); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + plugin.getPluginLogger().warn("Error occurred when creating expansion instance.", e); + } + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/scheduler/BukkitSchedulerAdapter.java b/core/src/main/java/net/momirealms/customfishing/bukkit/scheduler/BukkitSchedulerAdapter.java new file mode 100644 index 00000000..a78cb4c7 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/scheduler/BukkitSchedulerAdapter.java @@ -0,0 +1,52 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.bukkit.scheduler; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.bukkit.scheduler.impl.BukkitExecutor; +import net.momirealms.customfishing.bukkit.scheduler.impl.FoliaExecutor; +import net.momirealms.customfishing.common.helper.VersionHelper; +import net.momirealms.customfishing.common.plugin.scheduler.AbstractJavaScheduler; +import net.momirealms.customfishing.common.plugin.scheduler.RegionExecutor; +import org.bukkit.Location; + +public class BukkitSchedulerAdapter extends AbstractJavaScheduler { + protected RegionExecutor sync; + + public BukkitSchedulerAdapter(BukkitCustomFishingPlugin plugin) { + super(plugin); + if (VersionHelper.isFolia()) { + this.sync = new FoliaExecutor(plugin.getBoostrap()); + } else { + this.sync = new BukkitExecutor(plugin.getBoostrap()); + } + } + + @Override + public RegionExecutor sync() { + return this.sync; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/scheduler/impl/BukkitExecutor.java b/core/src/main/java/net/momirealms/customfishing/bukkit/scheduler/impl/BukkitExecutor.java new file mode 100644 index 00000000..af583f14 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/scheduler/impl/BukkitExecutor.java @@ -0,0 +1,64 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.bukkit.scheduler.impl; + +import net.momirealms.customfishing.common.plugin.scheduler.RegionExecutor; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.plugin.Plugin; + +public class BukkitExecutor implements RegionExecutor { + + private final Plugin plugin; + + public BukkitExecutor(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public void run(Runnable r, Location l) { + Bukkit.getScheduler().runTask(plugin, r); + } + + @Override + public SchedulerTask runLater(Runnable r, long delayTicks, Location l) { + if (delayTicks == 0) { + if (Bukkit.isPrimaryThread()) { + r.run(); + return () -> {}; + } else { + return Bukkit.getScheduler().runTask(plugin, r)::cancel; + } + } + return Bukkit.getScheduler().runTaskLater(plugin, r, delayTicks)::cancel; + } + + @Override + public SchedulerTask runRepeating(Runnable r, long delayTicks, long period, Location l) { + return Bukkit.getScheduler().runTaskTimer(plugin, r, delayTicks, period)::cancel; + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/scheduler/impl/FoliaExecutor.java b/core/src/main/java/net/momirealms/customfishing/bukkit/scheduler/impl/FoliaExecutor.java new file mode 100644 index 00000000..29d3d4c6 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/scheduler/impl/FoliaExecutor.java @@ -0,0 +1,74 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.bukkit.scheduler.impl; + +import net.momirealms.customfishing.common.plugin.scheduler.RegionExecutor; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.plugin.Plugin; + +import java.util.Optional; + +public class FoliaExecutor implements RegionExecutor { + + private final Plugin plugin; + + public FoliaExecutor(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public void run(Runnable r, Location l) { + Optional.ofNullable(l).ifPresentOrElse(loc -> Bukkit.getRegionScheduler().execute(plugin, loc, r), () -> Bukkit.getGlobalRegionScheduler().execute(plugin, r)); + } + + @Override + public SchedulerTask runLater(Runnable r, long delayTicks, Location l) { + if (l == null) { + if (delayTicks == 0) { + return Bukkit.getGlobalRegionScheduler().runDelayed(plugin, scheduledTask -> r.run(), delayTicks)::cancel; + } else { + return Bukkit.getGlobalRegionScheduler().run(plugin, scheduledTask -> r.run())::cancel; + } + } else { + if (delayTicks == 0) { + return Bukkit.getRegionScheduler().run(plugin, l, scheduledTask -> r.run())::cancel; + } else { + return Bukkit.getRegionScheduler().runDelayed(plugin, l, scheduledTask -> r.run(), delayTicks)::cancel; + } + } + } + + @Override + public SchedulerTask runRepeating(Runnable r, long delayTicks, long period, Location l) { + if (l == null) { + return Bukkit.getGlobalRegionScheduler().runAtFixedRate(plugin, scheduledTask -> r.run(), delayTicks, period)::cancel; + } else { + return Bukkit.getRegionScheduler().runAtFixedRate(plugin, l, scheduledTask -> r.run(), delayTicks, period)::cancel; + } + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/sender/BukkitSenderFactory.java b/core/src/main/java/net/momirealms/customfishing/bukkit/sender/BukkitSenderFactory.java new file mode 100644 index 00000000..bb71b1f2 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/sender/BukkitSenderFactory.java @@ -0,0 +1,112 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customfishing.bukkit.sender; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.text.Component; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.common.sender.Sender; +import net.momirealms.customfishing.common.sender.SenderFactory; +import net.momirealms.customfishing.common.util.Tristate; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.RemoteConsoleCommandSender; +import org.bukkit.entity.Player; + +import java.util.UUID; + +public class BukkitSenderFactory extends SenderFactory { + private final BukkitAudiences audiences; + + public BukkitSenderFactory(BukkitCustomFishingPlugin plugin) { + super(plugin); + this.audiences = BukkitAudiences.create(plugin.getBoostrap()); + } + + @Override + protected String getName(CommandSender sender) { + if (sender instanceof Player) { + return sender.getName(); + } + return Sender.CONSOLE_NAME; + } + + @Override + protected UUID getUniqueId(CommandSender sender) { + if (sender instanceof Player) { + return ((Player) sender).getUniqueId(); + } + return Sender.CONSOLE_UUID; + } + + @Override + public Audience getAudience(CommandSender sender) { + return this.audiences.sender(sender); + } + + @Override + protected void sendMessage(CommandSender sender, Component message) { + // we can safely send async for players and the console - otherwise, send it sync + if (sender instanceof Player || sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) { + getAudience(sender).sendMessage(message); + } else { + getPlugin().getScheduler().executeSync(() -> getAudience(sender).sendMessage(message)); + } + } + + @Override + protected Tristate getPermissionValue(CommandSender sender, String node) { + if (sender.hasPermission(node)) { + return Tristate.TRUE; + } else if (sender.isPermissionSet(node)) { + return Tristate.FALSE; + } else { + return Tristate.UNDEFINED; + } + } + + @Override + protected boolean hasPermission(CommandSender sender, String node) { + return sender.hasPermission(node); + } + + @Override + protected void performCommand(CommandSender sender, String command) { + getPlugin().getBoostrap().getServer().dispatchCommand(sender, command); + } + + @Override + protected boolean isConsole(CommandSender sender) { + return sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender; + } + + @Override + public void close() { + super.close(); + this.audiences.close(); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/statistic/StatisticsManagerImpl.java b/core/src/main/java/net/momirealms/customfishing/bukkit/statistic/BukkitStatisticsManager.java similarity index 59% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/statistic/StatisticsManagerImpl.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/statistic/BukkitStatisticsManager.java index 732dd3bf..93025ef4 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/statistic/StatisticsManagerImpl.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/statistic/BukkitStatisticsManager.java @@ -15,54 +15,38 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.statistic; +package net.momirealms.customfishing.bukkit.statistic; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.user.OnlineUser; -import net.momirealms.customfishing.api.manager.StatisticsManager; -import net.momirealms.customfishing.api.mechanic.statistic.Statistics; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.statistic.StatisticsManager; import org.bukkit.configuration.file.YamlConfiguration; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.util.*; -public class StatisticsManagerImpl implements StatisticsManager { +public class BukkitStatisticsManager implements StatisticsManager { - private final CustomFishingPlugin plugin; - private final HashMap> categoryMap; + private final BukkitCustomFishingPlugin plugin; + private final Map> categoryMap = new HashMap<>(); - public StatisticsManagerImpl(CustomFishingPlugin plugin) { + public BukkitStatisticsManager(BukkitCustomFishingPlugin plugin) { this.plugin = plugin; - this.categoryMap = new HashMap<>(); } + @Override public void load() { this.loadCategoriesFromPluginFolder(); + for (Map.Entry> entry : categoryMap.entrySet()) { + plugin.debug("Category: {" + entry.getKey() + "} Members: " + entry.getValue()); + } } + @Override public void unload() { this.categoryMap.clear(); } - - public void disable() { - 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; - return onlineUser.getStatistics(); - } - + @SuppressWarnings("DuplicatedCode") public void loadCategoriesFromPluginFolder() { Deque fileDeque = new ArrayDeque<>(); @@ -70,7 +54,7 @@ public class StatisticsManagerImpl implements StatisticsManager { File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type); if (!typeFolder.exists()) { if (!typeFolder.mkdirs()) return; - plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false); + plugin.getBoostrap().saveResource("contents" + File.separator + type + File.separator + "default.yml", false); } fileDeque.push(typeFolder); while (!fileDeque.isEmpty()) { @@ -95,15 +79,9 @@ 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. - */ + @NotNull @Override - @Nullable - public List getCategory(String key) { - return categoryMap.get(key); + public List getCategoryMembers(String key) { + return categoryMap.getOrDefault(key, List.of()); } } diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/BukkitStorageManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/BukkitStorageManager.java new file mode 100644 index 00000000..380c5635 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/BukkitStorageManager.java @@ -0,0 +1,365 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.storage; + +import com.google.gson.JsonSyntaxException; +import dev.dejvokep.boostedyaml.YamlDocument; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.storage.DataStorageProvider; +import net.momirealms.customfishing.api.storage.StorageManager; +import net.momirealms.customfishing.api.storage.StorageType; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.api.storage.user.UserData; +import net.momirealms.customfishing.bukkit.storage.method.database.nosql.MongoDBProvider; +import net.momirealms.customfishing.bukkit.storage.method.database.nosql.RedisManager; +import net.momirealms.customfishing.bukkit.storage.method.database.sql.H2Provider; +import net.momirealms.customfishing.bukkit.storage.method.database.sql.MariaDBProvider; +import net.momirealms.customfishing.bukkit.storage.method.database.sql.MySQLProvider; +import net.momirealms.customfishing.bukkit.storage.method.database.sql.SQLiteProvider; +import net.momirealms.customfishing.bukkit.storage.method.file.JsonProvider; +import net.momirealms.customfishing.bukkit.storage.method.file.YAMLProvider; +import net.momirealms.customfishing.common.helper.GsonHelper; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class BukkitStorageManager implements StorageManager, Listener { + + private final BukkitCustomFishingPlugin plugin; + private DataStorageProvider dataSource; + private StorageType previousType; + private final ConcurrentHashMap onlineUserMap; + private final HashSet locked; + private boolean hasRedis; + private RedisManager redisManager; + private String serverID; + private SchedulerTask timerSaveTask; + + public BukkitStorageManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + this.locked = new HashSet<>(); + this.onlineUserMap = new ConcurrentHashMap<>(); + Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap()); + } + + @Override + public void reload() { + YamlDocument config = plugin.getConfigManager().loadConfig("database.yml"); + this.serverID = 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(); + this.previousType = storageType; + switch (storageType) { + case H2 -> this.dataSource = new H2Provider(plugin); + case JSON -> this.dataSource = new JsonProvider(plugin); + case YAML -> this.dataSource = new YAMLProvider(plugin); + case SQLite -> this.dataSource = new SQLiteProvider(plugin); + case MySQL -> this.dataSource = new MySQLProvider(plugin); + case MariaDB -> this.dataSource = new MariaDBProvider(plugin); + case MongoDB -> this.dataSource = new MongoDBProvider(plugin); + } + if (this.dataSource != null) this.dataSource.initialize(config); + else plugin.getPluginLogger().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(config); + } + + // 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.cancel(); + } + + // Schedule periodic data saving if dataSaveInterval is configured + if (ConfigManager.dataSaveInterval() > 0) + this.timerSaveTask = this.plugin.getScheduler().asyncRepeating( + () -> { + long time1 = System.currentTimeMillis(); + this.dataSource.updateManyPlayersData(this.onlineUserMap.values(), !ConfigManager.lockData()); + if (ConfigManager.logDataSaving()) + plugin.getPluginLogger().info("Data Saved for online players. Took " + (System.currentTimeMillis() - time1) + "ms."); + }, + ConfigManager.dataSaveInterval(), + ConfigManager.dataSaveInterval(), + TimeUnit.SECONDS + ); + } + + /** + * Disables the storage manager and cleans up resources. + */ + @Override + public void disable() { + HandlerList.unregisterAll(this); + this.dataSource.updateManyPlayersData(onlineUserMap.values(), true); + this.onlineUserMap.clear(); + if (this.dataSource != null) + this.dataSource.disable(); + if (this.redisManager != null) + this.redisManager.disable(); + } + + @NotNull + @Override + public String getServerID() { + return serverID; + } + + @NotNull + @Override + public Optional getOnlineUser(UUID uuid) { + return Optional.ofNullable(onlineUserMap.get(uuid)); + } + + @NotNull + @Override + public Collection getOnlineUsers() { + return onlineUserMap.values(); + } + + @Override + public CompletableFuture> getOfflineUserData(UUID uuid, boolean lock) { + CompletableFuture> optionalDataFuture = dataSource.getPlayerData(uuid, lock); + return optionalDataFuture.thenCompose(optionalUser -> { + if (optionalUser.isEmpty()) { + return CompletableFuture.completedFuture(Optional.empty()); + } + PlayerData data = optionalUser.get(); + return CompletableFuture.completedFuture(Optional.of(UserData.builder() + .data(data) + .build())); + }); + } + + @Override + public CompletableFuture saveUserData(UserData userData, boolean unlock) { + return dataSource.updatePlayerData(userData.uuid(), userData.toPlayerData(), unlock); + } + + @NotNull + @Override + public DataStorageProvider 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(); + UUID uuid = player.getUniqueId(); + locked.add(uuid); + if (!hasRedis) { + waitLock(uuid, 1); + } else { + plugin.getScheduler().asyncLater(() -> redisManager.getChangeServer(uuid).thenAccept(changeServer -> { + if (!changeServer) { + waitLock(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(); + UUID uuid = player.getUniqueId(); + if (locked.contains(uuid)) + return; + + UserData onlineUser = onlineUserMap.remove(uuid); + if (onlineUser == null) return; + PlayerData data = onlineUser.toPlayerData(); + + if (hasRedis) { + redisManager.setChangeServer(uuid).thenRun( + () -> redisManager.updatePlayerData(uuid, data, true).thenRun( + () -> dataSource.updatePlayerData(uuid, data, true).thenAccept( + result -> { + if (result) locked.remove(uuid); + }))); + } else { + dataSource.updatePlayerData(uuid, data, true).thenAccept( + result -> { + if (result) locked.remove(uuid); + }); + } + } + + /** + * Runnable task for asynchronously retrieving data from Redis. + * Retries up to 6 times and cancels the task if the player is offline. + */ + private class RedisGetDataTask implements Runnable { + + private final UUID uuid; + private int triedTimes; + private final SchedulerTask task; + + public RedisGetDataTask(UUID uuid) { + this.uuid = uuid; + this.task = plugin.getScheduler().asyncRepeating(this, 0, 333, TimeUnit.MILLISECONDS); + } + + @Override + public void run() { + triedTimes++; + Player player = Bukkit.getPlayer(uuid); + if (player == null || !player.isOnline()) { + // offline + task.cancel(); + return; + } + if (triedTimes >= 6) { + waitLock(uuid, 3); + return; + } + redisManager.getPlayerData(uuid, false).thenAccept(optionalData -> { + if (optionalData.isPresent()) { + addOnlineUser(player, optionalData.get()); + task.cancel(); + if (ConfigManager.lockData()) dataSource.lockOrUnlockPlayerData(uuid, true); + } + }); + } + } + + /** + * 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. + */ + private void waitLock(UUID uuid, int times) { + plugin.getScheduler().asyncLater(() -> { + var player = Bukkit.getPlayer(uuid); + if (player == null || !player.isOnline()) + return; + if (times > 3) { + plugin.getPluginLogger().warn("Tried 3 times when getting data for " + uuid + ". Giving up."); + return; + } + this.dataSource.getPlayerData(uuid, ConfigManager.lockData()).thenAccept(optionalData -> { + // Data should not be empty + if (optionalData.isEmpty()) { + plugin.getPluginLogger().severe("Unexpected error: Data is null"); + return; + } + + if (optionalData.get().locked()) { + waitLock(uuid, times + 1); + } else { + try { + addOnlineUser(player, optionalData.get()); + } catch (Exception e) { + plugin.getPluginLogger().severe("Unexpected error: " + e.getMessage(), e); + } + } + }); + }, 1, TimeUnit.SECONDS); + } + + private void addOnlineUser(Player player, PlayerData playerData) { + this.locked.remove(player.getUniqueId()); + this.onlineUserMap.put(player.getUniqueId(), UserData.builder() + .data(playerData) + // update the name + .name(player.getName()) + .build()); + } + + @Override + public boolean isRedisEnabled() { + return hasRedis; + } + + @Nullable + public RedisManager getRedisManager() { + return redisManager; + } + + @NotNull + @Override + public byte[] toBytes(@NotNull PlayerData data) { + return toJson(data).getBytes(StandardCharsets.UTF_8); + } + + @Override + @NotNull + public String toJson(@NotNull PlayerData data) { + return GsonHelper.get().toJson(data); + } + + @NotNull + @Override + public PlayerData fromJson(String json) { + try { + return GsonHelper.get().fromJson(json, PlayerData.class); + } catch (JsonSyntaxException e) { + plugin.getPluginLogger().severe("Failed to parse PlayerData from json"); + plugin.getPluginLogger().info("Json: " + json); + throw new RuntimeException(e); + } + } + + @Override + @NotNull + public PlayerData fromBytes(byte[] data) { + return fromJson(new String(data, StandardCharsets.UTF_8)); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/AbstractStorage.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/AbstractStorage.java similarity index 65% rename from plugin/src/main/java/net/momirealms/customfishing/storage/method/AbstractStorage.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/AbstractStorage.java index d9306d06..8d74782b 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/AbstractStorage.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/AbstractStorage.java @@ -15,12 +15,13 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.storage.method; +package net.momirealms.customfishing.bukkit.storage.method; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.DataStorageInterface; -import net.momirealms.customfishing.api.data.PlayerData; -import net.momirealms.customfishing.api.data.user.OfflineUser; +import dev.dejvokep.boostedyaml.YamlDocument; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.storage.DataStorageProvider; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.api.storage.user.UserData; import java.time.Instant; import java.util.Collection; @@ -30,16 +31,16 @@ 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 { +public abstract class AbstractStorage implements DataStorageProvider { - protected CustomFishingPlugin plugin; + protected BukkitCustomFishingPlugin plugin; - public AbstractStorage(CustomFishingPlugin plugin) { + public AbstractStorage(BukkitCustomFishingPlugin plugin) { this.plugin = plugin; } @Override - public void initialize() { + public void initialize(YamlDocument config) { // This method can be overridden in subclasses to perform initialization tasks specific to the storage type. } @@ -58,19 +59,12 @@ public abstract class AbstractStorage implements DataStorageInterface { } @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 updateManyPlayersData(Collection users, boolean unlock) { + for (UserData user : users) { + this.updatePlayerData(user.uuid(), user.toPlayerData(), unlock); } } - /** - * 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 } diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/MongoDBImpl.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/MongoDBProvider.java similarity index 66% rename from plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/MongoDBImpl.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/MongoDBProvider.java index 1bfd7562..cc490fc6 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/MongoDBImpl.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/MongoDBProvider.java @@ -15,52 +15,44 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.storage.method.database.nosql; +package net.momirealms.customfishing.bukkit.storage.method.database.nosql; import com.mongodb.*; import com.mongodb.client.*; import com.mongodb.client.model.*; import com.mongodb.client.result.UpdateResult; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.PlayerData; -import net.momirealms.customfishing.api.data.StorageType; -import net.momirealms.customfishing.api.data.user.OfflineUser; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.setting.CFConfig; -import net.momirealms.customfishing.storage.method.AbstractStorage; +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.storage.StorageType; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.api.storage.user.UserData; +import net.momirealms.customfishing.bukkit.storage.method.AbstractStorage; import org.bson.Document; import org.bson.UuidRepresentation; import org.bson.conversions.Bson; import org.bson.types.Binary; import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -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 { +public class MongoDBProvider extends AbstractStorage { private MongoClient mongoClient; private MongoDatabase database; private String collectionPrefix; - public MongoDBImpl(CustomFishingPlugin plugin) { + public MongoDBProvider(BukkitCustomFishingPlugin plugin) { 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"); - ConfigurationSection section = config.getConfigurationSection("MongoDB"); + public void initialize(YamlDocument config) { + Section section = config.getSection("MongoDB"); if (section == null) { - LogUtils.warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one."); + plugin.getPluginLogger().warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one."); return; } @@ -89,9 +81,6 @@ 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) { @@ -123,51 +112,41 @@ 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>(); - plugin.getScheduler().runTaskAsync(() -> { + plugin.getScheduler().async().execute(() -> { MongoCollection collection = database.getCollection(getCollectionName("data")); Document doc = collection.find(Filters.eq("uuid", uuid)).first(); if (doc == null) { if (Bukkit.getPlayer(uuid) != null) { if (lock) lockOrUnlockPlayerData(uuid, true); - future.complete(Optional.of(PlayerData.empty())); + var data = PlayerData.empty(); + data.uuid(uuid); + future.complete(Optional.of(data)); } else { future.complete(Optional.empty()); } } else { - if (doc.getInteger("lock") != 0 && getCurrentSeconds() - CFConfig.dataSaveInterval <= doc.getInteger("lock")) { - future.complete(Optional.of(PlayerData.LOCKED)); + Binary binary = (Binary) doc.get("data"); + PlayerData data = plugin.getStorageManager().fromBytes(binary.getData()); + data.uuid(uuid); + if (doc.getInteger("lock") != 0 && getCurrentSeconds() - ConfigManager.dataSaveInterval() <= doc.getInteger("lock")) { + data.locked(true); + future.complete(Optional.of(data)); return; } - Binary binary = (Binary) doc.get("data"); if (lock) lockOrUnlockPlayerData(uuid, true); - future.complete(Optional.of(plugin.getStorageManager().fromBytes(binary.getData()))); + future.complete(Optional.of(data)); } }); 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(); - plugin.getScheduler().runTaskAsync(() -> { + plugin.getScheduler().async().execute(() -> { MongoCollection collection = database.getCollection(getCollectionName("data")); try { Document query = new Document("uuid", uuid); @@ -184,39 +163,27 @@ 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) { + public void updateManyPlayersData(Collection users, boolean unlock) { MongoCollection collection = database.getCollection(getCollectionName("data")); try { int lock = unlock ? 0 : getCurrentSeconds(); var list = users.stream().map(it -> new UpdateOneModel( - new Document("uuid", it.getUUID()), + new Document("uuid", it.uuid()), Updates.combine( Updates.set("lock", lock), - Updates.set("data", new Binary(plugin.getStorageManager().toBytes(it.getPlayerData()))) + Updates.set("data", new Binary(plugin.getStorageManager().toBytes(it.toPlayerData()))) ), new UpdateOptions().upsert(true) ) ).toList(); - if (list.size() == 0) return; + if (list.isEmpty()) return; collection.bulkWrite(list); } catch (MongoException e) { - LogUtils.warn("Failed to update data for online players", e); + plugin.getPluginLogger().warn("Failed to update data for online players", e); } } - /** - * 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 lockOrUnlockPlayerData(UUID uuid, boolean lock) { MongoCollection collection = database.getCollection(getCollectionName("data")); @@ -226,18 +193,12 @@ public class MongoDBImpl extends AbstractStorage { UpdateOptions options = new UpdateOptions().upsert(true); collection.updateOne(query, updates, options); } catch (MongoException e) { - LogUtils.warn("Failed to lock data for " + uuid, e); + plugin.getPluginLogger().warn("Failed to lock data for " + uuid, e); } } - /** - * 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) { + public Set getUniqueUsers() { // no legacy files Set uuids = new HashSet<>(); MongoCollection collection = database.getCollection(getCollectionName("data")); @@ -249,7 +210,7 @@ public class MongoDBImpl extends AbstractStorage { } } } catch (MongoException e) { - LogUtils.warn("Failed to get unique data.", e); + plugin.getPluginLogger().warn("Failed to get unique data.", e); } return uuids; } diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/RedisManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/RedisManager.java similarity index 73% rename from plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/RedisManager.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/RedisManager.java index 3fb89303..f65b45ff 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/RedisManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/nosql/RedisManager.java @@ -15,16 +15,17 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.storage.method.database.nosql; +package net.momirealms.customfishing.bukkit.storage.method.database.nosql; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.PlayerData; -import net.momirealms.customfishing.api.data.StorageType; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.setting.CFConfig; -import net.momirealms.customfishing.storage.method.AbstractStorage; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteStreams; +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.storage.StorageType; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.bukkit.storage.method.AbstractStorage; import org.jetbrains.annotations.NotNull; import redis.clients.jedis.*; import redis.clients.jedis.exceptions.JedisException; @@ -36,9 +37,6 @@ import java.time.Duration; import java.util.*; 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; @@ -51,7 +49,7 @@ public class RedisManager extends AbstractStorage { private BlockingThreadTask threadTask; private boolean isNewerThan5; - public RedisManager(CustomFishingPlugin plugin) { + public RedisManager(BukkitCustomFishingPlugin plugin) { super(plugin); instance = this; } @@ -78,11 +76,10 @@ public class RedisManager extends AbstractStorage { * Initialize the Redis connection and configuration based on the plugin's YAML configuration. */ @Override - public void initialize() { - YamlConfiguration config = plugin.getConfig("database.yml"); - ConfigurationSection section = config.getConfigurationSection("Redis"); + public void initialize(YamlDocument config) { + Section section = config.getSection("Redis"); if (section == null) { - LogUtils.warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one."); + plugin.getPluginLogger().warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one."); return; } @@ -90,7 +87,7 @@ public class RedisManager extends AbstractStorage { jedisPoolConfig.setTestWhileIdle(true); jedisPoolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(30000)); jedisPoolConfig.setNumTestsPerEvictionRun(-1); - jedisPoolConfig.setMinEvictableIdleTime(Duration.ofMillis(section.getInt("MinEvictableIdleTimeMillis",1800000))); + jedisPoolConfig.setMinEvictableIdleDuration(Duration.ofMillis(section.getInt("MinEvictableIdleTimeMillis", 1800000))); jedisPoolConfig.setMaxTotal(section.getInt("MaxTotal",8)); jedisPoolConfig.setMaxIdle(section.getInt("MaxIdle",8)); jedisPoolConfig.setMinIdle(section.getInt("MinIdle",1)); @@ -109,9 +106,9 @@ public class RedisManager extends AbstractStorage { String info; try (Jedis jedis = jedisPool.getResource()) { info = jedis.info(); - LogUtils.info("Redis server connected."); + plugin.getPluginLogger().info("Redis server connected."); } catch (JedisException e) { - LogUtils.warn("Failed to connect redis.", e); + plugin.getPluginLogger().warn("Failed to connect redis.", e); return; } @@ -141,12 +138,9 @@ public class RedisManager extends AbstractStorage { /** * Send a message to Redis on a specified channel. * - * @param server The Redis channel to send the message to. * @param message The message to send. */ - public void publishRedisMessage(@NotNull String server, @NotNull String message) { - message = server + ";" + message; - plugin.debug("Sent Redis message: " + message); + public void publishRedisMessage(@NotNull String message) { if (isNewerThan5) { try (Jedis jedis = jedisPool.getResource()) { HashMap messages = new HashMap<>(); @@ -189,30 +183,28 @@ public class RedisManager extends AbstractStorage { thread.start(); } - private static void handleMessage(String message) { - CustomFishingPlugin.get().debug("Received Redis message: " + message); - String[] split = message.split(";"); - String server = split[0]; - if (!CFConfig.serverGroup.contains(server)) { + private void handleMessage(String message) { + ByteArrayDataInput input = ByteStreams.newDataInput(message.getBytes(StandardCharsets.UTF_8)); + String server = input.readUTF(); + if (!ConfigManager.serverGroup().equals(server)) return; - } - String action = split[1]; - CustomFishingPlugin.get().getScheduler().runTaskAsync(() -> { + String type = input.readUTF(); + if (type.equals("competition")) { + String action = input.readUTF(); switch (action) { case "start" -> { - // start competition for all the servers that connected to redis - CustomFishingPlugin.get().getCompetitionManager().startCompetition(split[2], true, null); + plugin.getCompetitionManager().startCompetition(input.readUTF(), true, null); } case "end" -> { - if (CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition() != null) - CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition().end(true); + if (plugin.getCompetitionManager().getOnGoingCompetition() != null) + plugin.getCompetitionManager().getOnGoingCompetition().end(true); } case "stop" -> { - if (CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition() != null) - CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition().stop(true); + if (plugin.getCompetitionManager().getOnGoingCompetition() != null) + plugin.getCompetitionManager().getOnGoingCompetition().stop(true); } } - }); + } } @Override @@ -228,7 +220,7 @@ public class RedisManager extends AbstractStorage { */ public CompletableFuture setChangeServer(UUID uuid) { var future = new CompletableFuture(); - plugin.getScheduler().runTaskAsync(() -> { + plugin.getScheduler().async().execute(() -> { try (Jedis jedis = jedisPool.getResource()) { jedis.setex( getRedisKey("cf_server", uuid), @@ -237,7 +229,6 @@ public class RedisManager extends AbstractStorage { ); } future.complete(null); - plugin.debug("Server data set for " + uuid); }); return future; } @@ -250,16 +241,14 @@ public class RedisManager extends AbstractStorage { */ public CompletableFuture getChangeServer(UUID uuid) { var future = new CompletableFuture(); - plugin.getScheduler().runTaskAsync(() -> { + plugin.getScheduler().async().execute(() -> { try (Jedis jedis = jedisPool.getResource()) { byte[] key = getRedisKey("cf_server", uuid); if (jedis.get(key) != null) { jedis.del(key); future.complete(true); - plugin.debug("Server data retrieved for " + uuid + "; value: true"); } else { future.complete(false); - plugin.debug("Server data retrieved for " + uuid + "; value: false"); } } }); @@ -276,21 +265,18 @@ public class RedisManager extends AbstractStorage { @Override public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { var future = new CompletableFuture>(); - plugin.getScheduler().runTaskAsync(() -> { + plugin.getScheduler().async().execute(() -> { try (Jedis jedis = jedisPool.getResource()) { byte[] key = getRedisKey("cf_data", uuid); byte[] data = jedis.get(key); jedis.del(key); if (data != null) { future.complete(Optional.of(plugin.getStorageManager().fromBytes(data))); - plugin.debug("Redis data retrieved for " + uuid + "; normal data"); } else { future.complete(Optional.empty()); - plugin.debug("Redis data retrieved for " + uuid + "; empty data"); } } catch (Exception e) { future.complete(Optional.empty()); - LogUtils.warn("Failed to get redis data for " + uuid, e); } }); return future; @@ -307,7 +293,7 @@ public class RedisManager extends AbstractStorage { @Override public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData, boolean ignore) { var future = new CompletableFuture(); - plugin.getScheduler().runTaskAsync(() -> { + plugin.getScheduler().async().execute(() -> { try (Jedis jedis = jedisPool.getResource()) { jedis.setex( getRedisKey("cf_data", uuid), @@ -315,24 +301,15 @@ public class RedisManager extends AbstractStorage { plugin.getStorageManager().toBytes(playerData) ); future.complete(true); - plugin.debug("Redis data set for " + uuid); } catch (Exception e) { future.complete(false); - LogUtils.warn("Failed to set redis data for player " + uuid, e); } }); 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) { + public Set getUniqueUsers() { return new HashSet<>(); } @@ -351,17 +328,17 @@ public class RedisManager extends AbstractStorage { return STREAM; } - private static boolean isRedisNewerThan5(String version) { + private boolean isRedisNewerThan5(String version) { String[] split = version.split("\\."); int major = Integer.parseInt(split[0]); if (major < 7) { - LogUtils.warn(String.format("Detected that you are running an outdated Redis server. v%s. ", version)); - LogUtils.warn("It's recommended to update to avoid security vulnerabilities!"); + plugin.getPluginLogger().warn(String.format("Detected that you are running an outdated Redis server. v%s. ", version)); + plugin.getPluginLogger().warn("It's recommended to update to avoid security vulnerabilities!"); } return major >= 5; } - private static String parseRedisVersion(String info) { + private String parseRedisVersion(String info) { for (String line : info.split("\n")) { if (line.startsWith("redis_version:")) { return line.split(":")[1]; @@ -388,7 +365,7 @@ public class RedisManager extends AbstractStorage { if (connection != null) { var messages = connection.xread(XReadParams.xReadParams().count(1).block(2000), map); connection.close(); - if (messages != null && messages.size() != 0) { + if (messages != null && !messages.isEmpty()) { for (Map.Entry> message : messages) { if (message.getKey().equals(getStream())) { var value = message.getValue().get(0).getFields().get("value"); @@ -400,7 +377,7 @@ public class RedisManager extends AbstractStorage { Thread.sleep(2000); } } catch (Exception e) { - LogUtils.warn("Failed to connect redis. Try reconnecting 10s later",e); + plugin.getPluginLogger().warn("Failed to connect redis. Try reconnecting 10s later",e); try { Thread.sleep(10000); } catch (InterruptedException ex) { diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/AbstractHikariDatabase.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/AbstractHikariDatabase.java new file mode 100644 index 00000000..cc8c46d4 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/AbstractHikariDatabase.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.storage.method.database.sql; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.storage.StorageType; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; + +public abstract class AbstractHikariDatabase extends AbstractSQLDatabase { + + private HikariDataSource dataSource; + private final String driverClass; + private final String sqlBrand; + + public AbstractHikariDatabase(BukkitCustomFishingPlugin plugin) { + super(plugin); + this.driverClass = getStorageType() == StorageType.MariaDB ? "org.mariadb.jdbc.Driver" : "com.mysql.cj.jdbc.Driver"; + this.sqlBrand = getStorageType() == StorageType.MariaDB ? "MariaDB" : "MySQL"; + try { + Class.forName(this.driverClass); + } catch (ClassNotFoundException e1) { + if (getStorageType() == StorageType.MariaDB) { + plugin.getPluginLogger().warn("No MariaDB driver is found"); + } else if (getStorageType() == StorageType.MySQL) { + try { + Class.forName("com.mysql.jdbc.Driver"); + } catch (ClassNotFoundException e2) { + plugin.getPluginLogger().warn("No MySQL driver is found"); + } + } + } + } + + @Override + public void initialize(YamlDocument config) { + Section section = config.getSection(sqlBrand); + + if (section == null) { + plugin.getPluginLogger().warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one."); + return; + } + + super.tablePrefix = section.getString("table-prefix", "customfishing"); + HikariConfig hikariConfig = new HikariConfig(); + hikariConfig.setUsername(section.getString("user", "root")); + hikariConfig.setPassword(section.getString("password", "pa55w0rd")); + hikariConfig.setJdbcUrl(String.format("jdbc:%s://%s:%s/%s%s", + sqlBrand.toLowerCase(Locale.ENGLISH), + section.getString("host", "localhost"), + section.getString("port", "3306"), + section.getString("database", "minecraft"), + section.getString("connection-parameters") + )); + hikariConfig.setDriverClassName(driverClass); + hikariConfig.setMaximumPoolSize(section.getInt("Pool-Settings.max-pool-size", 10)); + hikariConfig.setMinimumIdle(section.getInt("Pool-Settings.min-idle", 10)); + hikariConfig.setMaxLifetime(section.getLong("Pool-Settings.max-lifetime", 180000L)); + hikariConfig.setConnectionTimeout(section.getLong("Pool-Settings.time-out", 20000L)); + hikariConfig.setPoolName("CustomFishingHikariPool"); + try { + hikariConfig.setKeepaliveTime(section.getLong("Pool-Settings.keep-alive-time", 60000L)); + } catch (NoSuchMethodError ignored) { + } + + final Properties properties = new Properties(); + properties.putAll( + Map.of("cachePrepStmts", "true", + "prepStmtCacheSize", "250", + "prepStmtCacheSqlLimit", "2048", + "useServerPrepStmts", "true", + "useLocalSessionState", "true", + "useLocalTransactionState", "true" + )); + properties.putAll( + Map.of( + "rewriteBatchedStatements", "true", + "cacheResultSetMetadata", "true", + "cacheServerConfiguration", "true", + "elideSetAutoCommits", "true", + "maintainTimeStats", "false") + ); + hikariConfig.setDataSourceProperties(properties); + dataSource = new HikariDataSource(hikariConfig); + super.createTableIfNotExist(); + } + + @Override + public void disable() { + if (dataSource != null && !dataSource.isClosed()) + dataSource.close(); + } + + @Override + public Connection getConnection() throws SQLException { + return dataSource.getConnection(); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractSQLDatabase.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/AbstractSQLDatabase.java similarity index 70% rename from plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractSQLDatabase.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/AbstractSQLDatabase.java index ca8bfee9..8bbfb10e 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractSQLDatabase.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/AbstractSQLDatabase.java @@ -15,14 +15,13 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.storage.method.database.sql; +package net.momirealms.customfishing.bukkit.storage.method.database.sql; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.PlayerData; -import net.momirealms.customfishing.api.data.user.OfflineUser; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.setting.CFConfig; -import net.momirealms.customfishing.storage.method.AbstractStorage; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.api.storage.user.UserData; +import net.momirealms.customfishing.bukkit.storage.method.AbstractStorage; import org.bukkit.Bukkit; import org.jetbrains.annotations.NotNull; @@ -40,7 +39,7 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { protected String tablePrefix; - public AbstractSQLDatabase(CustomFishingPlugin plugin) { + public AbstractSQLDatabase(BukkitCustomFishingPlugin plugin) { super(plugin); } @@ -63,12 +62,12 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { statement.execute(tableCreationStatement); } } catch (SQLException e) { - LogUtils.warn("Failed to create tables", e); + plugin.getPluginLogger().warn("Failed to create tables", e); } } catch (SQLException e) { - LogUtils.warn("Failed to get sql connection", e); + plugin.getPluginLogger().warn("Failed to get sql connection", e); } catch (IOException e) { - LogUtils.warn("Failed to get schema resource", e); + plugin.getPluginLogger().warn("Failed to get schema resource", e); } } @@ -80,7 +79,7 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { * @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")) + return replaceSchemaPlaceholder(new String(Objects.requireNonNull(plugin.getBoostrap().getResource("schema/" + fileName + ".sql")) .readAllBytes(), StandardCharsets.UTF_8)).split(";"); } @@ -113,18 +112,11 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { 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) { var future = new CompletableFuture>(); - plugin.getScheduler().runTaskAsync(() -> { + plugin.getScheduler().async().execute(() -> { try ( Connection connection = getConnection(); PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data"))) @@ -132,47 +124,44 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { statement.setString(1, uuid.toString()); ResultSet rs = statement.executeQuery(); if (rs.next()) { - if (lock) { - int lockValue = rs.getInt(2); - if (lockValue != 0 && getCurrentSeconds() - CFConfig.dataSaveInterval <= lockValue) { - connection.close(); - future.complete(Optional.of(PlayerData.LOCKED)); - LogUtils.warn("Player " + uuid + "'s data is locked. Retrying..."); - return; - } - } final Blob blob = rs.getBlob("data"); final byte[] dataByteArray = blob.getBytes(1, (int) blob.length()); blob.free(); + PlayerData data = plugin.getStorageManager().fromBytes(dataByteArray); + data.uuid(uuid); + if (lock) { + int lockValue = rs.getInt(2); + if (lockValue != 0 && getCurrentSeconds() - ConfigManager.dataSaveInterval() <= lockValue) { + connection.close(); + data.locked(true); + future.complete(Optional.of(data)); + plugin.getPluginLogger().warn("Player " + uuid + "'s data is locked. Retrying..."); + return; + } + } if (lock) lockOrUnlockPlayerData(uuid, true); - future.complete(Optional.of(plugin.getStorageManager().fromBytes(dataByteArray))); + future.complete(Optional.of(data)); } else if (Bukkit.getPlayer(uuid) != null) { + // the player is online var data = PlayerData.empty(); + data.uuid(uuid); insertPlayerData(uuid, data, lock); future.complete(Optional.of(data)); } else { future.complete(Optional.empty()); } } catch (SQLException e) { - LogUtils.warn("Failed to get " + uuid + "'s data.", e); + plugin.getPluginLogger().warn("Failed to get " + uuid + "'s data.", e); future.completeExceptionally(e); } }); 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(); - plugin.getScheduler().runTaskAsync(() -> { + plugin.getScheduler().async().execute(() -> { try ( Connection connection = getConnection(); PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data"))) @@ -182,51 +171,37 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { statement.setString(3, uuid.toString()); statement.executeUpdate(); future.complete(true); - plugin.debug("SQL data saved for " + uuid + "; unlock: " + unlock); } catch (SQLException e) { - LogUtils.warn("Failed to update " + uuid + "'s data.", e); + plugin.getPluginLogger().warn("Failed to update " + uuid + "'s data.", e); future.completeExceptionally(e); } }); 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) { + public void updateManyPlayersData(Collection users, boolean unlock) { String sql = String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data")); try (Connection connection = getConnection()) { connection.setAutoCommit(false); try (PreparedStatement statement = connection.prepareStatement(sql)) { - for (OfflineUser user : users) { + for (UserData user : users) { statement.setInt(1, unlock ? 0 : getCurrentSeconds()); - statement.setBlob(2, new ByteArrayInputStream(plugin.getStorageManager().toBytes(user.getPlayerData()))); - statement.setString(3, user.getUUID().toString()); + statement.setBlob(2, new ByteArrayInputStream(plugin.getStorageManager().toBytes(user.toPlayerData()))); + statement.setString(3, user.uuid().toString()); statement.addBatch(); } statement.executeBatch(); connection.commit(); } catch (SQLException e) { connection.rollback(); - LogUtils.warn("Failed to update data for online players", e); + plugin.getPluginLogger().warn("Failed to update data for online players", e); } } catch (SQLException e) { - LogUtils.warn("Failed to get connection when saving online players' data", e); + plugin.getPluginLogger().warn("Failed to get connection when saving online players' data", e); } } - /** - * 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(); @@ -237,16 +212,10 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { statement.setBlob(3, new ByteArrayInputStream(plugin.getStorageManager().toBytes(playerData))); statement.execute(); } catch (SQLException e) { - LogUtils.warn("Failed to insert " + uuid + "'s data.", e); + plugin.getPluginLogger().warn("Failed to insert " + uuid + "'s data.", e); } } - /** - * 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 lockOrUnlockPlayerData(UUID uuid, boolean lock) { try ( @@ -257,22 +226,14 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { statement.setString(2, uuid.toString()); statement.execute(); } catch (SQLException e) { - LogUtils.warn("Failed to lock " + uuid + "'s data.", e); + plugin.getPluginLogger().warn("Failed to lock " + uuid + "'s data.", e); } } - /** - * 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(); - plugin.getScheduler().runTaskAsync(() -> { + plugin.getScheduler().async().execute(() -> { try ( Connection connection = getConnection(); PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data"))) @@ -286,23 +247,17 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { future.complete(true); } } catch (SQLException e) { - LogUtils.warn("Failed to get " + uuid + "'s data.", e); + plugin.getPluginLogger().warn("Failed to get " + uuid + "'s data.", e); } }); 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) { + public Set getUniqueUsers() { Set uuids = new HashSet<>(); try (Connection connection = getConnection(); - PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_ALL_UUID, legacy ? getTableName("fishingbag") : getTableName("data")))) { + PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_ALL_UUID, getTableName("data")))) { try (ResultSet rs = statement.executeQuery()) { while (rs.next()) { UUID uuid = UUID.fromString(rs.getString("uuid")); @@ -310,7 +265,7 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { } } } catch (SQLException e) { - LogUtils.warn("Failed to get unique data.", e); + plugin.getPluginLogger().warn("Failed to get unique data.", e); } return uuids; } diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/H2Impl.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/H2Provider.java similarity index 76% rename from plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/H2Impl.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/H2Provider.java index f28e029c..b64a711a 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/H2Impl.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/H2Provider.java @@ -15,13 +15,12 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.storage.method.database.sql; +package net.momirealms.customfishing.bukkit.storage.method.database.sql; -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.StorageType; -import net.momirealms.customfishing.libraries.dependencies.Dependency; -import org.bukkit.configuration.file.YamlConfiguration; +import dev.dejvokep.boostedyaml.YamlDocument; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.storage.StorageType; +import net.momirealms.customfishing.common.dependency.Dependency; import java.io.File; import java.lang.reflect.Method; @@ -31,13 +30,13 @@ import java.util.EnumSet; /** * An implementation of AbstractSQLDatabase that uses the H2 embedded database for player data storage. */ -public class H2Impl extends AbstractSQLDatabase { +public class H2Provider extends AbstractSQLDatabase { private Object connectionPool; private Method disposeMethod; private Method getConnectionMethod; - public H2Impl(CustomFishingPlugin plugin) { + public H2Provider(BukkitCustomFishingPlugin plugin) { super(plugin); } @@ -45,13 +44,12 @@ public class H2Impl extends AbstractSQLDatabase { * Initialize the H2 database and connection pool based on the configuration. */ @Override - public void initialize() { - YamlConfiguration config = plugin.getConfig("database.yml"); + public void initialize(YamlDocument config) { File databaseFile = new File(plugin.getDataFolder(), config.getString("H2.file", "data.db")); super.tablePrefix = config.getString("H2.table-prefix", "customfishing"); final String url = String.format("jdbc:h2:%s", databaseFile.getAbsolutePath()); - ClassLoader classLoader = ((CustomFishingPluginImpl) plugin).getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.H2_DRIVER)); + ClassLoader classLoader = plugin.getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.H2_DRIVER)); try { Class connectionClass = classLoader.loadClass("org.h2.jdbcx.JdbcConnectionPool"); Method createPoolMethod = connectionClass.getMethod("create", String.class, String.class, String.class); @@ -65,9 +63,6 @@ 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/MariaDBImpl.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/MariaDBProvider.java similarity index 71% rename from plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/MariaDBImpl.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/MariaDBProvider.java index b209b168..90a85178 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/MariaDBImpl.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/MariaDBProvider.java @@ -15,14 +15,14 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.storage.method.database.sql; +package net.momirealms.customfishing.bukkit.storage.method.database.sql; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.StorageType; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.storage.StorageType; -public class MariaDBImpl extends AbstractHikariDatabase { +public class MariaDBProvider extends AbstractHikariDatabase { - public MariaDBImpl(CustomFishingPlugin plugin) { + public MariaDBProvider(BukkitCustomFishingPlugin plugin) { super(plugin); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/MySQLImpl.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/MySQLProvider.java similarity index 71% rename from plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/MySQLImpl.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/MySQLProvider.java index ff644f84..33d05333 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/MySQLImpl.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/MySQLProvider.java @@ -15,14 +15,14 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.storage.method.database.sql; +package net.momirealms.customfishing.bukkit.storage.method.database.sql; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.StorageType; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.storage.StorageType; -public class MySQLImpl extends AbstractHikariDatabase { +public class MySQLProvider extends AbstractHikariDatabase { - public MySQLImpl(CustomFishingPlugin plugin) { + public MySQLProvider(BukkitCustomFishingPlugin plugin) { super(plugin); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/SQLiteImpl.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/SQLiteProvider.java similarity index 65% rename from plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/SQLiteImpl.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/SQLiteProvider.java index 9e4ae73e..dc6095ad 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/SQLiteImpl.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/database/sql/SQLiteProvider.java @@ -15,18 +15,16 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.storage.method.database.sql; +package net.momirealms.customfishing.bukkit.storage.method.database.sql; -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.PlayerData; -import net.momirealms.customfishing.api.data.StorageType; -import net.momirealms.customfishing.api.data.user.OfflineUser; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.libraries.dependencies.Dependency; -import net.momirealms.customfishing.setting.CFConfig; +import dev.dejvokep.boostedyaml.YamlDocument; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.storage.StorageType; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.api.storage.user.UserData; +import net.momirealms.customfishing.common.dependency.Dependency; import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; import java.io.File; import java.lang.reflect.Constructor; @@ -37,25 +35,19 @@ import java.sql.SQLException; import java.util.*; import java.util.concurrent.CompletableFuture; -/** - * An implementation of AbstractSQLDatabase that uses the SQLite database for player data storage. - */ -public class SQLiteImpl extends AbstractSQLDatabase { +public class SQLiteProvider extends AbstractSQLDatabase { private Connection connection; private File databaseFile; private Constructor connectionConstructor; - public SQLiteImpl(CustomFishingPlugin plugin) { + public SQLiteProvider(BukkitCustomFishingPlugin plugin) { super(plugin); } - /** - * Initialize the SQLite database and connection based on the configuration. - */ @Override - public void initialize() { - ClassLoader classLoader = ((CustomFishingPluginImpl) plugin).getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.SQLITE_DRIVER, Dependency.SLF4J_SIMPLE, Dependency.SLF4J_API)); + public void initialize(YamlDocument config) { + ClassLoader classLoader = plugin.getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.SQLITE_DRIVER, Dependency.SLF4J_SIMPLE, Dependency.SLF4J_API)); try { Class connectionClass = classLoader.loadClass("org.sqlite.jdbc4.JDBC4Connection"); connectionConstructor = connectionClass.getConstructor(String.class, String.class, Properties.class); @@ -63,15 +55,11 @@ public class SQLiteImpl extends AbstractSQLDatabase { throw new RuntimeException(e); } - YamlConfiguration config = plugin.getConfig("database.yml"); this.databaseFile = new File(plugin.getDataFolder(), config.getString("SQLite.file", "data") + ".db"); super.tablePrefix = config.getString("SQLite.table-prefix", "customfishing"); super.createTableIfNotExist(); } - /** - * Disable the SQLite database by closing the connection. - */ @Override public void disable() { try { @@ -113,18 +101,11 @@ public class SQLiteImpl extends AbstractSQLDatabase { } } - /** - * 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) { var future = new CompletableFuture>(); - plugin.getScheduler().runTaskAsync(() -> { + plugin.getScheduler().async().execute(() -> { try ( Connection connection = getConnection(); PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data"))) @@ -132,42 +113,38 @@ public class SQLiteImpl extends AbstractSQLDatabase { statement.setString(1, uuid.toString()); ResultSet rs = statement.executeQuery(); if (rs.next()) { + final byte[] dataByteArray = rs.getBytes("data"); + PlayerData data = plugin.getStorageManager().fromBytes(dataByteArray); + data.uuid(uuid); int lockValue = rs.getInt(2); - if (lockValue != 0 && getCurrentSeconds() - CFConfig.dataSaveInterval <= lockValue) { + if (lockValue != 0 && getCurrentSeconds() - ConfigManager.dataSaveInterval() <= lockValue) { connection.close(); - future.complete(Optional.of(PlayerData.LOCKED)); + data.locked(true); + future.complete(Optional.of(data)); return; } - final byte[] dataByteArray = rs.getBytes("data"); if (lock) lockOrUnlockPlayerData(uuid, true); - future.complete(Optional.of(plugin.getStorageManager().fromBytes(dataByteArray))); + future.complete(Optional.of(data)); } else if (Bukkit.getPlayer(uuid) != null) { var data = PlayerData.empty(); + data.uuid(uuid); insertPlayerData(uuid, data, lock); future.complete(Optional.of(data)); } else { future.complete(Optional.empty()); } } catch (SQLException e) { - LogUtils.warn("Failed to get " + uuid + "'s data.", e); + plugin.getPluginLogger().warn("Failed to get " + uuid + "'s data.", e); future.completeExceptionally(e); } }); 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(); - plugin.getScheduler().runTaskAsync(() -> { + plugin.getScheduler().async().execute(() -> { try ( Connection connection = getConnection(); PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data"))) @@ -178,49 +155,36 @@ public class SQLiteImpl extends AbstractSQLDatabase { statement.executeUpdate(); future.complete(true); } catch (SQLException e) { - LogUtils.warn("Failed to update " + uuid + "'s data.", e); + plugin.getPluginLogger().warn("Failed to update " + uuid + "'s data.", e); future.completeExceptionally(e); } }); 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) { + public void updateManyPlayersData(Collection users, boolean unlock) { String sql = String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data")); try (Connection connection = getConnection()) { connection.setAutoCommit(false); try (PreparedStatement statement = connection.prepareStatement(sql)) { - for (OfflineUser user : users) { + for (UserData user : users) { statement.setInt(1, unlock ? 0 : getCurrentSeconds()); - statement.setBytes(2, plugin.getStorageManager().toBytes(user.getPlayerData())); - statement.setString(3, user.getUUID().toString()); + statement.setBytes(2, plugin.getStorageManager().toBytes(user.toPlayerData())); + statement.setString(3, user.uuid().toString()); statement.addBatch(); } statement.executeBatch(); connection.commit(); } catch (SQLException e) { connection.rollback(); - LogUtils.warn("Failed to update bag data for online players", e); + plugin.getPluginLogger().warn("Failed to update bag data for online players", e); } } catch (SQLException e) { - LogUtils.warn("Failed to get connection when saving online players' data", e); + plugin.getPluginLogger().warn("Failed to get connection when saving online players' data", e); } } - /** - * 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 ( @@ -232,7 +196,7 @@ public class SQLiteImpl extends AbstractSQLDatabase { statement.setBytes(3, plugin.getStorageManager().toBytes(playerData)); statement.execute(); } catch (SQLException e) { - LogUtils.warn("Failed to insert " + uuid + "'s data.", e); + plugin.getPluginLogger().warn("Failed to insert " + uuid + "'s data.", e); } } } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/JsonImpl.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/JsonProvider.java similarity index 89% rename from plugin/src/main/java/net/momirealms/customfishing/storage/method/file/JsonImpl.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/JsonProvider.java index 9f4b2f7f..4432c4d4 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/JsonImpl.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/JsonProvider.java @@ -15,13 +15,13 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.storage.method.file; +package net.momirealms.customfishing.bukkit.storage.method.file; import com.google.gson.Gson; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.PlayerData; -import net.momirealms.customfishing.api.data.StorageType; -import net.momirealms.customfishing.storage.method.AbstractStorage; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.storage.StorageType; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.bukkit.storage.method.AbstractStorage; import org.bukkit.Bukkit; import java.io.File; @@ -38,10 +38,10 @@ import java.util.concurrent.CompletableFuture; /** * A data storage implementation that uses JSON files to store player data. */ -public class JsonImpl extends AbstractStorage { +public class JsonProvider extends AbstractStorage { @SuppressWarnings("ResultOfMethodCallIgnored") - public JsonImpl(CustomFishingPlugin plugin) { + public JsonProvider(BukkitCustomFishingPlugin plugin) { super(plugin); File folder = new File(plugin.getDataFolder(), "data"); if (!folder.exists()) folder.mkdirs(); @@ -60,6 +60,7 @@ public class JsonImpl extends AbstractStorage { playerData = readFromJsonFile(file, PlayerData.class); } else if (Bukkit.getPlayer(uuid) != null) { playerData = PlayerData.empty(); + playerData.uuid(uuid); } else { playerData = null; } @@ -130,7 +131,7 @@ public class JsonImpl extends AbstractStorage { // Retrieve a set of unique user UUIDs based on JSON data files in the 'data' folder. @Override - public Set getUniqueUsers(boolean legacy) { + public Set getUniqueUsers() { // No legacy files File folder = new File(plugin.getDataFolder(), "data"); Set uuids = new HashSet<>(); diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/YAMLProvider.java b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/YAMLProvider.java new file mode 100644 index 00000000..e844d779 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/storage/method/file/YAMLProvider.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.storage.method.file; + +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.storage.StorageType; +import net.momirealms.customfishing.api.storage.data.EarningData; +import net.momirealms.customfishing.api.storage.data.InventoryData; +import net.momirealms.customfishing.api.storage.data.PlayerData; +import net.momirealms.customfishing.api.storage.data.StatisticData; +import net.momirealms.customfishing.bukkit.storage.method.AbstractStorage; +import org.bukkit.Bukkit; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +public class YAMLProvider extends AbstractStorage { + + @SuppressWarnings("ResultOfMethodCallIgnored") + public YAMLProvider(BukkitCustomFishingPlugin plugin) { + super(plugin); + File folder = new File(plugin.getDataFolder(), "data"); + if (!folder.exists()) folder.mkdirs(); + } + + @Override + public StorageType getStorageType() { + 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"); + } + + @Override + public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { + File dataFile = getPlayerDataFile(uuid); + if (!dataFile.exists()) { + if (Bukkit.getPlayer(uuid) != null) { + var data = PlayerData.empty(); + data.uuid(uuid); + return CompletableFuture.completedFuture(Optional.of(data)); + } else { + return CompletableFuture.completedFuture(Optional.empty()); + } + } + YamlDocument data = plugin.getConfigManager().loadData(dataFile); + PlayerData playerData = PlayerData.builder() + .bag(new InventoryData(data.getString("bag", ""), data.getInt("size", 9))) + .earnings(new EarningData(data.getDouble("earnings"), data.getInt("date"))) + .statistics(getStatistics(data.getSection("stats"))) + .name(data.getString("name", "")) + .build(); + return CompletableFuture.completedFuture(Optional.of(playerData)); + } + + @Override + public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData, boolean ignore) { + YamlConfiguration data = new YamlConfiguration(); + data.set("name", playerData.name()); + data.set("bag", playerData.bagData().serialized); + data.set("size", playerData.bagData().size); + data.set("date", playerData.earningData().date); + data.set("earnings", playerData.earningData().earnings); + ConfigurationSection section = data.createSection("stats"); + ConfigurationSection amountSection = section.createSection("amount"); + ConfigurationSection sizeSection = section.createSection("size"); + for (Map.Entry entry : playerData.statistics().amountMap.entrySet()) { + amountSection.set(entry.getKey(), entry.getValue()); + } + for (Map.Entry entry : playerData.statistics().sizeMap.entrySet()) { + sizeSection.set(entry.getKey(), entry.getValue()); + } + try { + data.save(getPlayerDataFile(uuid)); + } catch (IOException e) { + plugin.getPluginLogger().warn("Failed to save player data", e); + } + return CompletableFuture.completedFuture(true); + } + + @Override + public Set getUniqueUsers() { + File folder = new File(plugin.getDataFolder(), "data"); + Set uuids = new HashSet<>(); + if (folder.exists()) { + File[] files = folder.listFiles(); + if (files != null) { + for (File file : files) { + uuids.add(UUID.fromString(file.getName().substring(0, file.getName().length() - 4))); + } + } + } + return uuids; + } + + private StatisticData getStatistics(Section section) { + HashMap amountMap = new HashMap<>(); + HashMap sizeMap = new HashMap<>(); + if (section == null) { + return new StatisticData(amountMap, sizeMap); + } + Section amountSection = section.getSection("amount"); + if (amountSection != null) { + for (Map.Entry entry : amountSection.getStringRouteMappedValues(false).entrySet()) { + amountMap.put(entry.getKey(), (Integer) entry.getValue()); + } + } + Section sizeSection = section.getSection("size"); + if (sizeSection != null) { + for (Map.Entry entry : sizeSection.getStringRouteMappedValues(false).entrySet()) { + sizeMap.put(entry.getKey(), ((Double) entry.getValue()).floatValue()); + } + } + return new StatisticData(amountMap, sizeMap); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/totem/ActivatedTotem.java b/core/src/main/java/net/momirealms/customfishing/bukkit/totem/ActivatedTotem.java new file mode 100644 index 00000000..2bd8d67b --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/totem/ActivatedTotem.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.totem; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.context.ContextKeys; +import net.momirealms.customfishing.api.mechanic.totem.TotemConfig; +import net.momirealms.customfishing.api.mechanic.totem.TotemParticle; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +public class ActivatedTotem { + + private final List subTasks; + private final Location coreLocation; + private final TotemConfig totemConfig; + private final long expireTime; + private final Context context; + private final double radius; + + public ActivatedTotem(Player activator, Location coreLocation, TotemConfig config) { + this.context = Context.player(activator, true) + .arg(ContextKeys.LOCATION, coreLocation) + .arg(ContextKeys.X, coreLocation.getBlockX()) + .arg(ContextKeys.Y, coreLocation.getBlockY()) + .arg(ContextKeys.Z, coreLocation.getBlockZ()) + .arg(ContextKeys.ID, config.id()); + this.subTasks = new ArrayList<>(); + this.expireTime = (long) (System.currentTimeMillis() + config.duration().evaluate(context) * 1000L); + this.coreLocation = coreLocation.clone().add(0.5,0,0.5); + this.totemConfig = config; + this.radius = config.radius().evaluate(context); + for (TotemParticle particleSetting : config.particleSettings()) { + this.subTasks.add(particleSetting.start(coreLocation, radius)); + } + } + + public TotemConfig getTotemConfig() { + return totemConfig; + } + + public Location getCoreLocation() { + return coreLocation; + } + + public void cancel() { + for (SchedulerTask task : this.subTasks) { + task.cancel(); + } + this.subTasks.clear(); + } + + public long getExpireTime() { + return this.expireTime; + } + + public double getRadius() { + return radius; + } + + public void doTimerAction() { + this.context.arg(ContextKeys.TIME_LEFT, String.valueOf((expireTime - System.currentTimeMillis())/1000)); + BukkitCustomFishingPlugin.getInstance().getEventManager().getEventCarrier(totemConfig.id(), MechanicType.TOTEM) + .ifPresent(carrier -> carrier.trigger(context, ActionTrigger.TIMER)); + } +} diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/totem/BukkitTotemManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/totem/BukkitTotemManager.java new file mode 100644 index 00000000..f1597f31 --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/totem/BukkitTotemManager.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.totem; + +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.event.TotemActivateEvent; +import net.momirealms.customfishing.api.mechanic.MechanicType; +import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; +import net.momirealms.customfishing.api.mechanic.config.ConfigManager; +import net.momirealms.customfishing.api.mechanic.context.Context; +import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager; +import net.momirealms.customfishing.api.mechanic.totem.TotemConfig; +import net.momirealms.customfishing.api.mechanic.totem.TotemManager; +import net.momirealms.customfishing.api.mechanic.totem.block.TotemBlock; +import net.momirealms.customfishing.api.util.SimpleLocation; +import net.momirealms.customfishing.bukkit.util.LocationUtils; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class BukkitTotemManager implements TotemManager, Listener { + + private final BukkitCustomFishingPlugin plugin; + private final HashMap> block2Totem = new HashMap<>(); + private final HashMap id2Totem = new HashMap<>(); + private final List allMaterials = Arrays.stream(Material.values()).map(Enum::name).toList(); + private final ConcurrentHashMap activatedTotems = new ConcurrentHashMap<>(); + private SchedulerTask timerCheckTask; + + public BukkitTotemManager(BukkitCustomFishingPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void load() { + Bukkit.getPluginManager().registerEvents(this, plugin.getBoostrap()); + this.timerCheckTask = plugin.getScheduler().asyncRepeating(() -> { + long time = System.currentTimeMillis(); + ArrayList removed = new ArrayList<>(); + for (Map.Entry entry : activatedTotems.entrySet()) { + if (time > entry.getValue().getExpireTime()) { + removed.add(entry.getKey()); + entry.getValue().cancel(); + } else { + entry.getValue().doTimerAction(); + } + } + for (SimpleLocation simpleLocation : removed) { + activatedTotems.remove(simpleLocation); + } + }, 1, 1, TimeUnit.SECONDS); + plugin.debug("Loaded " + id2Totem.size() + " totems"); + } + + @Override + public void unload() { + HandlerList.unregisterAll(this); + for (ActivatedTotem activatedTotem : this.activatedTotems.values()) + activatedTotem.cancel(); + this.activatedTotems.clear(); + if (this.timerCheckTask != null) + this.timerCheckTask.cancel(); + this.block2Totem.clear(); + } + + @Override + public Collection getActivatedTotems(Location location) { + Collection activated = new ArrayList<>(); + double nearest = Double.MAX_VALUE; + String nearestTotemID = null; + for (ActivatedTotem activatedTotem : activatedTotems.values()) { + double distance = LocationUtils.getDistance(activatedTotem.getCoreLocation(), location); + if (distance < activatedTotem.getRadius()) { + activated.add(activatedTotem.getTotemConfig().id()); + if (nearest > distance) { + nearest = distance; + nearestTotemID = activatedTotem.getTotemConfig().id(); + } + } + } + if (nearestTotemID == null) return List.of(); + if (!ConfigManager.allowMultipleTotemType()) { + if (ConfigManager.allowSameTotemType()) { + String finalNearestTotemID = nearestTotemID; + activated.removeIf(element -> !element.equals(finalNearestTotemID)); + return activated; + } else { + return List.of(nearestTotemID); + } + } else { + if (ConfigManager.allowSameTotemType()) { + return activated; + } else { + return new HashSet<>(activated); + } + } + } + + @EventHandler + public void onBreakTotemCore(BlockBreakEvent event) { + if (event.isCancelled()) + return; + Location location = event.getBlock().getLocation(); + SimpleLocation simpleLocation = SimpleLocation.of(location); + ActivatedTotem activatedTotem = activatedTotems.remove(simpleLocation); + if (activatedTotem != null) + activatedTotem.cancel(); + } + + @EventHandler (ignoreCancelled = true) + public void onInteractBlock(PlayerInteractEvent event) { + if ( + event.isBlockInHand() || + event.getAction() != org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK || + event.getHand() != EquipmentSlot.HAND + ) + return; + + Block block = event.getClickedBlock(); + assert block != null; + String id = plugin.getBlockManager().getBlockID(block); + List configs = block2Totem.get(id); + if (configs == null) + return; + TotemConfig config = null; + for (TotemConfig temp : configs) { + if (temp.isRightPattern(block.getLocation())) { + config = temp; + break; + } + } + if (config == null) + return; + + String totemID = config.id(); + final Player player = event.getPlayer();; + Context context = Context.player(player); + Optional optionalEffectModifier = plugin.getEffectManager().getEffectModifier(totemID, MechanicType.TOTEM); + if (optionalEffectModifier.isPresent()) { + if (!RequirementManager.isSatisfied(context, optionalEffectModifier.get().requirements())) { + return; + } + } + + TotemActivateEvent totemActivateEvent = new TotemActivateEvent(player, block.getLocation(), config); + Bukkit.getPluginManager().callEvent(totemActivateEvent); + if (totemActivateEvent.isCancelled()) { + return; + } + + plugin.getEventManager().trigger(context, totemID, MechanicType.TOTEM, ActionTrigger.ACTIVATE); + + Location location = block.getLocation(); + ActivatedTotem activatedTotem = new ActivatedTotem(player, location, config); + SimpleLocation simpleLocation = SimpleLocation.of(location); + ActivatedTotem previous = this.activatedTotems.put(simpleLocation, activatedTotem); + if (previous != null) { + previous.cancel(); + } + } + + @Override + public boolean registerTotem(TotemConfig totem) { + if (id2Totem.containsKey(totem.id())) { + return false; + } + HashSet coreMaterials = new HashSet<>(); + for (TotemBlock totemBlock : totem.totemCore()) { + String text = totemBlock.getTypeCondition().getRawText(); + if (text.startsWith("*")) { + String sub = text.substring(1); + coreMaterials.addAll(allMaterials.stream().filter(it -> it.endsWith(sub)).toList()); + } else if (text.endsWith("*")) { + String sub = text.substring(0, text.length() - 1); + coreMaterials.addAll(allMaterials.stream().filter(it -> it.startsWith(sub)).toList()); + } else { + coreMaterials.add(text); + } + } + for (String material : coreMaterials) { + List configs = this.block2Totem.getOrDefault(material, new ArrayList<>()); + configs.add(totem); + this.block2Totem.put(material, configs); + } + id2Totem.put(totem.id(), totem); + return true; + } + + @NotNull + @Override + public Optional getTotem(String id) { + return Optional.ofNullable(id2Totem.get(id)); + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/particle/DustParticleSetting.java b/core/src/main/java/net/momirealms/customfishing/bukkit/totem/particle/DustParticleSetting.java similarity index 84% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/particle/DustParticleSetting.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/totem/particle/DustParticleSetting.java index ebc34e5b..d738ef93 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/particle/DustParticleSetting.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/totem/particle/DustParticleSetting.java @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.totem.particle; +package net.momirealms.customfishing.bukkit.totem.particle; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.scheduler.CancellableTask; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customfishing.common.util.Pair; import org.bukkit.Location; import org.bukkit.Particle; import org.bukkit.World; @@ -46,9 +46,9 @@ public class DustParticleSetting extends ParticleSetting { } @SuppressWarnings("DuplicatedCode") - public CancellableTask start(Location location, double radius) { + public SchedulerTask start(Location location, double radius) { World world = location.getWorld(); - return CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(() -> { + return BukkitCustomFishingPlugin.getInstance().getScheduler().asyncRepeating(() -> { for (Pair range : ranges) { for (double theta = range.left(); theta <= range.right(); theta += interval) { double r = expressionHorizontal.setVariable("theta", theta).setVariable("radius", radius).evaluate(); diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/particle/ParticleSetting.java b/core/src/main/java/net/momirealms/customfishing/bukkit/totem/particle/ParticleSetting.java similarity index 87% rename from plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/particle/ParticleSetting.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/totem/particle/ParticleSetting.java index 0ec64ac1..c29239a4 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/particle/ParticleSetting.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/totem/particle/ParticleSetting.java @@ -15,12 +15,12 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.mechanic.totem.particle; +package net.momirealms.customfishing.bukkit.totem.particle; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.common.Pair; +import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; import net.momirealms.customfishing.api.mechanic.totem.TotemParticle; -import net.momirealms.customfishing.api.scheduler.CancellableTask; +import net.momirealms.customfishing.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customfishing.common.util.Pair; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; import org.bukkit.Location; @@ -63,9 +63,9 @@ public class ParticleSetting implements TotemParticle { } @SuppressWarnings("DuplicatedCode") - public CancellableTask start(Location location, double radius) { + public SchedulerTask start(Location location, double radius) { World world = location.getWorld(); - return CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(() -> { + return BukkitCustomFishingPlugin.getInstance().getScheduler().asyncRepeating(() -> { for (Pair range : ranges) { for (double theta = range.left(); theta <= range.right(); theta += interval) { double r = expressionHorizontal.setVariable("theta", theta).setVariable("radius", radius).evaluate(); diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/util/ItemStackUtils.java b/core/src/main/java/net/momirealms/customfishing/bukkit/util/ItemStackUtils.java new file mode 100644 index 00000000..60e470fb --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/util/ItemStackUtils.java @@ -0,0 +1,435 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.util; + +import com.saicone.rtag.item.ItemTagStream; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customfishing.api.mechanic.item.ItemEditor; +import net.momirealms.customfishing.api.mechanic.item.tag.TagMap; +import net.momirealms.customfishing.api.mechanic.item.tag.TagValueType; +import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; +import net.momirealms.customfishing.api.mechanic.misc.value.TextValue; +import net.momirealms.customfishing.common.util.ArrayUtils; +import net.momirealms.customfishing.common.util.Pair; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; +import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.*; + +import static net.momirealms.customfishing.api.util.TagUtils.toTypeAndData; +import static net.momirealms.customfishing.common.util.ArrayUtils.splitValue; + +public class ItemStackUtils { + + private ItemStackUtils() {} + + public static ItemStack fromBase64(String base64) { + if (base64 == null || base64.isEmpty()) + return new ItemStack(Material.AIR); + ByteArrayInputStream inputStream; + try { + inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(base64)); + } catch (IllegalArgumentException e) { + return new ItemStack(Material.AIR); + } + ItemStack stack = null; + try (BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream)) { + stack = (ItemStack) dataInput.readObject(); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + return stack; + } + + public static String toBase64(ItemStack itemStack) { + if (itemStack == null || itemStack.getType() == Material.AIR) + return ""; + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try (BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream)) { + dataOutput.writeObject(itemStack); + byte[] byteArr = outputStream.toByteArray(); + dataOutput.close(); + outputStream.close(); + return Base64Coder.encodeLines(byteArr); + } catch (IOException e) { + e.printStackTrace(); + return ""; + } + } + + public static Map itemStackToMap(ItemStack itemStack) { + Map map = ItemTagStream.INSTANCE.toMap(itemStack); + map.remove("rtagDataVersion"); + map.remove("count"); + map.remove("id"); + map.put("material", itemStack.getType().name().toLowerCase(Locale.ENGLISH)); + map.put("amount", itemStack.getAmount()); + Object tag = map.remove("tags"); + if (tag != null) { + map.put("nbt", tag); + } + return map; + } + + private static void sectionToMap(Section section, Map outPut) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + HashMap map = new HashMap<>(); + outPut.put(entry.getKey(), map); + sectionToMap(inner, map); + } else { + outPut.put(entry.getKey(), entry.getValue()); + } + } + } + + @SuppressWarnings("UnstableApiUsage") + public static void sectionToComponentEditor(Section section, List itemEditors) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String component = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof Section inner) { + Map innerMap = new HashMap<>(); + sectionToMap(inner, innerMap); + TagMap tagMap = TagMap.of(innerMap); + itemEditors.add(((item, context) -> { + item.setComponent(component, tagMap.apply(context)); + })); + } else if (value instanceof List list) { + Object first = list.get(0); + if (first instanceof Map) { + ArrayList output = new ArrayList<>(); + for (Object o : list) { + Map innerMap = (Map) o; + TagMap tagMap = TagMap.of(innerMap); + output.add(tagMap); + } + itemEditors.add(((item, context) -> { + List> maps = output.stream().map(unparsed -> unparsed.apply(context)).toList(); + item.setComponent(component, maps); + })); + } else if (first instanceof String str) { + Pair pair = toTypeAndData(str); + switch (pair.left()) { + case INT -> { + List> values = new ArrayList<>(); + for (Object o : list) { + values.add(MathValue.auto(toTypeAndData((String) o).right())); + } + itemEditors.add(((item, context) -> { + List integers = values.stream().map(unparsed -> (int) unparsed.evaluate(context)).toList(); + item.setComponent(component, integers); + })); + } + case BYTE -> { + List> values = new ArrayList<>(); + for (Object o : list) { + values.add(MathValue.auto(toTypeAndData((String) o).right())); + } + itemEditors.add(((item, context) -> { + List bytes = values.stream().map(unparsed -> (byte) unparsed.evaluate(context)).toList(); + item.setComponent(component, bytes); + })); + } + case LONG -> { + List> values = new ArrayList<>(); + for (Object o : list) { + values.add(MathValue.auto(toTypeAndData((String) o).right())); + } + itemEditors.add(((item, context) -> { + List longs = values.stream().map(unparsed -> (long) unparsed.evaluate(context)).toList(); + item.setComponent(component, longs); + })); + } + case FLOAT -> { + List> values = new ArrayList<>(); + for (Object o : list) { + values.add(MathValue.auto(toTypeAndData((String) o).right())); + } + itemEditors.add(((item, context) -> { + List floats = values.stream().map(unparsed -> (float) unparsed.evaluate(context)).toList(); + item.setComponent(component, floats); + })); + } + case DOUBLE -> { + List> values = new ArrayList<>(); + for (Object o : list) { + values.add(MathValue.auto(toTypeAndData((String) o).right())); + } + itemEditors.add(((item, context) -> { + List doubles = values.stream().map(unparsed -> (double) unparsed.evaluate(context)).toList(); + item.setComponent(component, doubles); + })); + } + case STRING -> { + List> values = new ArrayList<>(); + for (Object o : list) { + values.add(TextValue.auto(toTypeAndData((String) o).right())); + } + itemEditors.add(((item, context) -> { + List texts = values.stream().map(unparsed -> unparsed.render(context)).toList(); + item.setComponent(component, texts); + })); + } + } + + } else { + itemEditors.add(((item, context) -> { + item.setComponent(component, list); + })); + } + } else if (value instanceof String str) { + Pair pair = toTypeAndData(str); + switch (pair.left()) { + case INT -> { + MathValue mathValue = MathValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.setComponent(component, (int) mathValue.evaluate(context)); + })); + } + case BYTE -> { + MathValue mathValue = MathValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.setComponent(component, (byte) mathValue.evaluate(context)); + })); + } + case FLOAT -> { + MathValue mathValue = MathValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.setComponent(component, (float) mathValue.evaluate(context)); + })); + } + case LONG -> { + MathValue mathValue = MathValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.setComponent(component, (long) mathValue.evaluate(context)); + })); + } + case SHORT -> { + MathValue mathValue = MathValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.setComponent(component, (short) mathValue.evaluate(context)); + })); + } + case DOUBLE -> { + MathValue mathValue = MathValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.setComponent(component, (double) mathValue.evaluate(context)); + })); + } + case STRING -> { + TextValue textValue = TextValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.setComponent(component, textValue.render(context)); + })); + } + case INTARRAY -> { + String[] split = splitValue(str); + int[] array = Arrays.stream(split).mapToInt(Integer::parseInt).toArray(); + itemEditors.add(((item, context) -> { + item.setComponent(component, array); + })); + } + case BYTEARRAY -> { + String[] split = splitValue(str); + byte[] bytes = new byte[split.length]; + for (int i = 0; i < split.length; i++){ + bytes[i] = Byte.parseByte(split[i]); + } + itemEditors.add(((item, context) -> { + item.setComponent(component, bytes); + })); + } + } + } else { + itemEditors.add(((item, context) -> { + item.setComponent(component, value); + })); + } + } + } + + // ugly codes, remaining improvements + public static void sectionToTagEditor(Section section, List itemEditors, String... route) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + Object value = entry.getValue(); + String key = entry.getKey(); + String[] currentRoute = ArrayUtils.appendElementToArray(route, key); + if (value instanceof Section inner) { + sectionToTagEditor(inner, itemEditors, currentRoute); + } else if (value instanceof List list) { + Object first = list.get(0); + if (first instanceof Map) { + List maps = new ArrayList<>(); + for (Object o : list) { + Map map = (Map) o; + maps.add(TagMap.of(map)); + } + itemEditors.add(((item, context) -> { + List> parsed = maps.stream().map(render -> render.apply(context)).toList(); + item.set(parsed, (Object[]) currentRoute); + })); + } else { + if (first instanceof String str) { + Pair pair = toTypeAndData(str); + switch (pair.left()) { + case INT -> { + List> values = new ArrayList<>(); + for (Object o : list) { + values.add(MathValue.auto(toTypeAndData((String) o).right())); + } + itemEditors.add(((item, context) -> { + List integers = values.stream().map(unparsed -> (int) unparsed.evaluate(context)).toList(); + item.set(integers, (Object[]) currentRoute); + })); + } + case BYTE -> { + List> values = new ArrayList<>(); + for (Object o : list) { + values.add(MathValue.auto(toTypeAndData((String) o).right())); + } + itemEditors.add(((item, context) -> { + List bytes = values.stream().map(unparsed -> (byte) unparsed.evaluate(context)).toList(); + item.set(bytes, (Object[]) currentRoute); + })); + } + case LONG -> { + List> values = new ArrayList<>(); + for (Object o : list) { + values.add(MathValue.auto(toTypeAndData((String) o).right())); + } + itemEditors.add(((item, context) -> { + List longs = values.stream().map(unparsed -> (long) unparsed.evaluate(context)).toList(); + item.set(longs, (Object[]) currentRoute); + })); + } + case FLOAT -> { + List> values = new ArrayList<>(); + for (Object o : list) { + values.add(MathValue.auto(toTypeAndData((String) o).right())); + } + itemEditors.add(((item, context) -> { + List floats = values.stream().map(unparsed -> (float) unparsed.evaluate(context)).toList(); + item.set(floats, (Object[]) currentRoute); + })); + } + case DOUBLE -> { + List> values = new ArrayList<>(); + for (Object o : list) { + values.add(MathValue.auto(toTypeAndData((String) o).right())); + } + itemEditors.add(((item, context) -> { + List doubles = values.stream().map(unparsed -> (double) unparsed.evaluate(context)).toList(); + item.set(doubles, (Object[]) currentRoute); + })); + } + case STRING -> { + List> values = new ArrayList<>(); + for (Object o : list) { + values.add(TextValue.auto(toTypeAndData((String) o).right())); + } + itemEditors.add(((item, context) -> { + List texts = values.stream().map(unparsed -> unparsed.render(context)).toList(); + item.set(texts, (Object[]) currentRoute); + })); + } + } + } else { + itemEditors.add(((item, context) -> { + item.set(list, (Object[]) currentRoute); + })); + } + } + } else if (value instanceof String str) { + Pair pair = toTypeAndData(str); + switch (pair.left()) { + case INT -> { + MathValue mathValue = MathValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.set((int) mathValue.evaluate(context), (Object[]) currentRoute); + })); + } + case BYTE -> { + MathValue mathValue = MathValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.set((byte) mathValue.evaluate(context), (Object[]) currentRoute); + })); + } + case LONG -> { + MathValue mathValue = MathValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.set((long) mathValue.evaluate(context), (Object[]) currentRoute); + })); + } + case SHORT -> { + MathValue mathValue = MathValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.set((short) mathValue.evaluate(context), (Object[]) currentRoute); + })); + } + case DOUBLE -> { + MathValue mathValue = MathValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.set((double) mathValue.evaluate(context), (Object[]) currentRoute); + })); + } + case FLOAT -> { + MathValue mathValue = MathValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.set((float) mathValue.evaluate(context), (Object[]) currentRoute); + })); + } + case STRING -> { + TextValue textValue = TextValue.auto(pair.right()); + itemEditors.add(((item, context) -> { + item.set(textValue.render(context), (Object[]) currentRoute); + })); + } + case INTARRAY -> { + String[] split = splitValue(str); + int[] array = Arrays.stream(split).mapToInt(Integer::parseInt).toArray(); + itemEditors.add(((item, context) -> { + item.set(array, (Object[]) currentRoute); + })); + } + case BYTEARRAY -> { + String[] split = splitValue(str); + byte[] bytes = new byte[split.length]; + for (int i = 0; i < split.length; i++){ + bytes[i] = Byte.parseByte(split[i]); + } + itemEditors.add(((item, context) -> { + item.set(bytes, (Object[]) currentRoute); + })); + } + } + } else { + itemEditors.add(((item, context) -> { + item.set(value, (Object[]) currentRoute); + })); + } + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/LocationUtils.java b/core/src/main/java/net/momirealms/customfishing/bukkit/util/LocationUtils.java similarity index 97% rename from plugin/src/main/java/net/momirealms/customfishing/util/LocationUtils.java rename to core/src/main/java/net/momirealms/customfishing/bukkit/util/LocationUtils.java index d4616720..10c9b0cf 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/LocationUtils.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/util/LocationUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customfishing.util; +package net.momirealms.customfishing.bukkit.util; import org.bukkit.Bukkit; import org.bukkit.Location; diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/util/PlayerUtils.java b/core/src/main/java/net/momirealms/customfishing/bukkit/util/PlayerUtils.java new file mode 100644 index 00000000..248bc47a --- /dev/null +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/util/PlayerUtils.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.bukkit.util; + +import net.momirealms.customfishing.common.util.RandomUtils; +import org.bukkit.Location; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; + +import static java.util.Objects.requireNonNull; + +public class PlayerUtils { + + public static void dropItem(@NotNull Player player, @NotNull ItemStack itemStack, boolean retainOwnership, boolean noPickUpDelay, boolean throwRandomly) { + requireNonNull(player, "player"); + requireNonNull(itemStack, "itemStack"); + Location location = player.getLocation().clone(); + Item item = player.getWorld().dropItem(player.getEyeLocation().clone().subtract(new Vector(0,0.3,0)), itemStack); + item.setPickupDelay(noPickUpDelay ? 0 : 40); + if (retainOwnership) { + item.setThrower(player.getUniqueId()); + } + if (throwRandomly) { + double d1 = RandomUtils.generateRandomDouble(0,1) * 0.5f; + double d2 = RandomUtils.generateRandomDouble(0,1) * (Math.PI * 2); + item.setVelocity(new Vector(-Math.sin(d2) * d1, 0.2f, Math.cos(d2) * d1)); + } else { + double d1 = Math.sin(location.getPitch() * (Math.PI/180)); + double d2 = RandomUtils.generateRandomDouble(0, 0.02); + double d3 = RandomUtils.generateRandomDouble(0,1) * (Math.PI * 2); + Vector vector = location.getDirection().multiply(0.3).setY(-d1 * 0.3 + 0.1 + (RandomUtils.generateRandomDouble(0,1) - RandomUtils.generateRandomDouble(0,1)) * 0.1); + vector.add(new Vector(Math.cos(d3) * d2, 0, Math.sin(d3) * d2)); + item.setVelocity(vector); + } + } + + public static int putItemsToInventory(Inventory inventory, ItemStack itemStack, int amount) { + ItemMeta meta = itemStack.getItemMeta(); + int maxStackSize = itemStack.getMaxStackSize(); + for (ItemStack other : inventory.getStorageContents()) { + if (other != null) { + if (other.getType() == itemStack.getType() && other.getItemMeta().equals(meta)) { + if (other.getAmount() < maxStackSize) { + int delta = maxStackSize - other.getAmount(); + if (amount > delta) { + other.setAmount(maxStackSize); + amount -= delta; + } else { + other.setAmount(amount + other.getAmount()); + return 0; + } + } + } + } + } + + if (amount > 0) { + for (ItemStack other : inventory.getStorageContents()) { + if (other == null) { + if (amount > maxStackSize) { + amount -= maxStackSize; + ItemStack cloned = itemStack.clone(); + cloned.setAmount(maxStackSize); + inventory.addItem(cloned); + } else { + ItemStack cloned = itemStack.clone(); + cloned.setAmount(amount); + inventory.addItem(cloned); + return 0; + } + } + } + } + + return amount; + } + + public static int giveItem(Player player, ItemStack itemStack, int amount) { + PlayerInventory inventory = player.getInventory(); + ItemMeta meta = itemStack.getItemMeta(); + int maxStackSize = itemStack.getMaxStackSize(); + if (amount > maxStackSize * 100) { + amount = maxStackSize * 100; + } + int actualAmount = amount; + for (ItemStack other : inventory.getStorageContents()) { + if (other != null) { + if (other.getType() == itemStack.getType() && other.getItemMeta().equals(meta)) { + if (other.getAmount() < maxStackSize) { + int delta = maxStackSize - other.getAmount(); + if (amount > delta) { + other.setAmount(maxStackSize); + amount -= delta; + } else { + other.setAmount(amount + other.getAmount()); + return actualAmount; + } + } + } + } + } + if (amount > 0) { + for (ItemStack other : inventory.getStorageContents()) { + if (other == null) { + if (amount > maxStackSize) { + amount -= maxStackSize; + ItemStack cloned = itemStack.clone(); + cloned.setAmount(maxStackSize); + inventory.addItem(cloned); + } else { + ItemStack cloned = itemStack.clone(); + cloned.setAmount(amount); + inventory.addItem(cloned); + return actualAmount; + } + } + } + } + + if (amount > 0) { + for (int i = 0; i < amount / maxStackSize; i++) { + ItemStack cloned = itemStack.clone(); + cloned.setAmount(maxStackSize); + player.getWorld().dropItem(player.getLocation(), cloned); + } + int left = amount % maxStackSize; + if (left != 0) { + ItemStack cloned = itemStack.clone(); + cloned.setAmount(left); + player.getWorld().dropItem(player.getLocation(), cloned); + } + } + + return actualAmount; + } +} diff --git a/core/src/main/resources/commands.yml b/core/src/main/resources/commands.yml new file mode 100644 index 00000000..63e514cb --- /dev/null +++ b/core/src/main/resources/commands.yml @@ -0,0 +1,186 @@ +# +# Don't change this +# +config-version: "${config_version}" + +# +# For safety reasons, editing this file requires a restart to apply +# + +# A command to reload the plugin +# Usage: [COMMAND] +reload: + enable: true + permission: customfishing.command.reload + usage: + - /customfishing reload + - /cfishing reload + +# A command designed for players to sell fish +# Usage: [COMMAND] +sellfish: + enable: true + permission: customfishing.sellfish + usage: + - /sellfish + +# A command designed for players to open the fishing bag +# Usage: [COMMAND] +fishingbag: + enable: true + permission: fishingbag.user + usage: + - /fishingbag + +# A command to get items +# Usage: [COMMAND] [id] +get_item: + enable: true + permission: customfishing.command.getitem + usage: + - /customfishing items get + - /cfishing items get + +# A command to give items +# Usage: [COMMAND] [id] +give_item: + enable: true + permission: customfishing.command.giveitem + usage: + - /customfishing items give + - /cfishing items give + +# A command to import items +# Usage: [COMMAND] [type] [id] +import_item: + enable: true + permission: customfishing.command.importitem + usage: + - /customfishing items import + - /cfishing items import + +# A command to stop the competition +# Usage: [COMMAND] +stop_competition: + enable: true + permission: customfishing.command.competition + usage: + - /customfishing competition stop + - /cfishing competition stop + +# A command to end the competition +# Usage: [COMMAND] [type] [id] +end_competition: + enable: true + permission: customfishing.command.competition + usage: + - /customfishing competition end + - /cfishing competition end + +# A command to start competitions +# Usage: [COMMAND] [id] +start_competition: + enable: true + permission: customfishing.command.competition + usage: + - /customfishing competition start + - /cfishing competition start + +# A command to open market for players +# Usage: [COMMAND] [player] +open_market: + enable: true + permission: customfishing.command.open.market + usage: + - /customfishing open market + - /cfishing open market + +# A command to open bag for players +# Usage: [COMMAND] [player] +open_bag: + enable: true + permission: customfishing.command.open.bag + usage: + - /customfishing open bag + - /cfishing open bag + +# A command to edit bag contents +# Usage: [COMMAND] [player] +edit_online_bag: + enable: true + permission: customfishing.command.edit.bag + usage: + - /customfishing fishingbag edit-online + - /cfishing fishingbag edit-online + +# A command to edit bag contents +# Usage: [COMMAND] [uuid] +edit_offline_bag: + enable: true + permission: customfishing.command.edit.bag + usage: + - /customfishing fishingbag edit-offline + - /cfishing fishingbag edit-offline + +# A command to unlock those locked data +# Usage: [COMMAND] [uuid] +data_unlock: + enable: true + permission: customfishing.command.data + usage: + - /customfishing data unlock + - /cfishing data unlock + +# A command to export the data +# Usage: [COMMAND] +data_export: + enable: true + permission: customfishing.command.data + usage: + - /customfishing data export + - /cfishing data export + +# A command to import the data +# Usage: [COMMAND] [file] +data_import: + enable: true + permission: customfishing.command.data + usage: + - /customfishing data import + - /cfishing data import + +# A command to set a player's fishing statistics +# Usage: [COMMAND] [player] [id] [type] [value] +statistics_set: + enable: true + permission: customfishing.command.statistics + usage: + - /customfishing statistics set + - /cfishing statistics set + +# A command to reset a player's fishing statistics +# Usage: [COMMAND] [player] +statistics_reset: + enable: true + permission: customfishing.command.statistics + usage: + - /customfishing statistics reset + - /cfishing statistics reset + +# A command to query a player's fishing statistics +# Usage: [COMMAND] [player] [type] +statistics_query: + enable: true + permission: customfishing.command.statistics + usage: + - /customfishing statistics query + - /cfishing statistics query + +# A command to manually add a player's fishing statistics +# Usage: [COMMAND] [player] [id] [type] [value] +statistics_add: + enable: true + permission: customfishing.command.statistics + usage: + - /customfishing statistics add + - /cfishing statistics add \ No newline at end of file diff --git a/plugin/src/main/resources/config.yml b/core/src/main/resources/config.yml similarity index 54% rename from plugin/src/main/resources/config.yml rename to core/src/main/resources/config.yml index 580fcb50..427fa637 100644 --- a/plugin/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -1,20 +1,13 @@ -# Developer: @Xiao-MoMi -# Wiki: https://mo-mi.gitbook.io/xiaomomi-plugins/ -config-version: '32' +# Don"t change this +config-version: '${config_version}' # Debug debug: false - # BStats metrics: true - # Check updates update-checker: true -# Language -# https://github.com/Xiao-MoMi/Custom-Fishing/tree/main/plugin/src/main/resources/messages -lang: en - # Mechanic settings mechanics: # Specifies the conditions required for the plugin mechanics to work. @@ -25,6 +18,15 @@ mechanics: type: '!world' value: - blacklist_world + # If you want to let some players skip games, you can set requirements that used to skip games + # We used `impossible` requirement here, so players should play the game if there exists + skip-game-requirements: + impossible_requirement: + type: 'impossible' + # Requirements for enabling auto-fishing + auto-fishing-requirements: + impossible_requirement: + type: 'impossible' # Configures global effects. This is useful if you want to give all the players certain effects based on certain conditions global-effects: @@ -55,7 +57,7 @@ mechanics: actions: actionbar_action: type: actionbar - value: '<#FFD700>[New Record] <#FFFFF0>You caught a(n) {nick} which is <#FFA500>{size}cm long!' + value: '<#FFD700>[New Record] <#FFFFF0>You caught a(n) {nick} which is <#FFA500>{size_formatted}cm long!' sound_action: type: sound value: @@ -85,7 +87,7 @@ mechanics: actions: actionbar_action: type: actionbar - value: 'You caught a(n) {nick} which is <#F5F5F5>{size}cm long! <#C0C0C0>(Best record: {record}cm)' + value: 'You caught a(n) {nick} which is <#F5F5F5>{size_formatted}cm long! <#C0C0C0>(Best record: {record_formatted}cm)' title_action: type: random-title value: @@ -130,7 +132,7 @@ mechanics: value: duration: 35 position: other - item: util:lava_effect + item: lava_effect priority_2: conditions: lava-fishing: false @@ -140,7 +142,7 @@ mechanics: value: duration: 35 position: other - item: util:water_effect + item: water_effect # Global properties which would help you reduce duplicated lines global-loot-property: @@ -151,16 +153,18 @@ mechanics: # Fishing bag is where players can store their baits, utils, hooks and rods (Loot optional) fishing-bag: - # Enable enable: true # Fishing bag container title bag-title: '{player}''s Fishing Bag' # Other whitelist-items whitelist-items: - fishing_rod - - # Can fishing bag store fishing loots? + # Decide the items that can be stored in bag can-store-loot: false + can-store-rod: true + can-store-bait: true + can-store-hook: true + can-store-util: true # Requirements for automatically collecting collect-requirements: permission: fishingbag.collectloot @@ -195,31 +199,184 @@ mechanics: message_action: type: message value: "<#EEE8AA>[Fishing Bag] Your fishing bag has been full." - - # Fishing wait time + market: + # Market GUI title + title: 'Fish Market' + # Whether to enable limitations + limitation: + enable: true + earnings: '10000' # You can use expressions here + # Market menu layout + layout: + - 'AAAAAAAAA' + - 'AIIIIIIIA' + - 'AIIIIIIIA' + - 'AIIIIIIIA' + - 'AAAABAAAA' + # Price formula (For CustomFishing loots) + price-formula: '{base} + {bonus} * {size}' + # Allow player to sell fish in bundles + allow-bundle: true + # Allow player to sell fish in shulker boxes + allow-shulker-box: true + # Item price (For vanilla items & other plugin items that have CustomModelData) + item-price: + # Vanilla Items + COD: 10 + PUFFERFISH: 10 + SALMON: 10 + TROPICAL_FISH: 10 + # PAPER (CustomModelData: 999) + PAPER:999: 5 + # Slots to put items in + item-slot: + symbol: 'I' + allow-items-with-no-price: true + # This is an icon that allows players to sell all the fish from their inventory and fishingbag + # You can enable it by putting the symbol into layout + sell-all-icons: + symbol: 'S' + # Should the fish in fishing bag be sold + fishingbag: true + allow-icon: + material: IRON_BLOCK + display: + name: '<#00CED1>Ship the fish' + lore: + - 'You will get {money_formatted} coins from the fish in inventory and bag' + action: + sound_action: + type: sound + value: + key: 'minecraft:block.amethyst_block.place' + source: 'player' + volume: 1 + pitch: 1 + message_action: + type: message + value: 'You earned {money_formatted} coins from the fish! You can get {rest_formatted} more coins from market today' + command_action: + type: command + value: 'money give {player} {money}' + # Requires Vault and any economy plugin + # money_action: + # type: give-money + # value: '{money}' + deny-icon: + material: REDSTONE_BLOCK + display: + name: 'Denied trade' + lore: + - 'Nothing to sell!' + action: + sound_action: + type: sound + value: + key: 'minecraft:entity.villager.no' + source: 'player' + volume: 1 + pitch: 1 + limit-icon: + material: REDSTONE_BLOCK + display: + name: 'Denied trade' + lore: + - 'The worth of items exceeds the money that can be earned for the rest of today!' + action: + sound_action: + type: sound + value: + key: 'minecraft:block.anvil.land' + source: 'player' + volume: 1 + pitch: 1 + # Sell icon + sell-icons: + symbol: 'B' + allow-icon: + material: IRON_BLOCK + display: + name: '<#00CED1>Ship the fish' + lore: + - 'You will get {money_formatted} coins from the fish' + action: + sound_action: + type: sound + value: + key: 'minecraft:block.amethyst_block.place' + source: 'player' + volume: 1 + pitch: 1 + message_action: + type: message + value: 'You earned {money_formatted} coins from the fish! You can get {rest_formatted} more coins from market today' + command_action: + type: command + value: 'money give {player} {money}' + # Requires Vault and any economy plugin + # money_action: + # type: give-money + # value: '{money}' + deny-icon: + material: REDSTONE_BLOCK + display: + name: 'Denied trade' + lore: + - 'Nothing to sell!' + action: + sound_action: + type: sound + value: + key: 'minecraft:entity.villager.no' + source: 'player' + volume: 1 + pitch: 1 + limit-icon: + material: REDSTONE_BLOCK + display: + name: 'Denied trade' + lore: + - 'The worth of items exceeds the money that can be earned for the rest of today!' + action: + sound_action: + type: sound + value: + key: 'minecraft:block.anvil.land' + source: 'player' + volume: 1 + pitch: 1 + # Decorative icons + decorative-icons: + glass-pane: + symbol: 'A' + material: BLACK_STAINED_GLASS_PANE + display: + name: ' ' # This section would take effect if you set "override-vanilla" to true # That also means vanilla mechanics for example lure enchantment - # would no longer take effect, so you have to configurate its effect - # in enchantment effects. + # would no longer take effect, so you have to configure its effect in CustomFishing fishing-wait-time: # override vanilla mechanic override-vanilla: false # ticks min-wait-time: 100 max-wait-time: 600 - # Lava fishing settings # To modify vanilla fishing time, you should edit paper-world-defaults.yml where there's a section called fishing-time-range lava-fishing: + enable: true + # ticks + min-wait-time: 100 + max-wait-time: 600 + void-fishing: + enable: true # ticks min-wait-time: 100 max-wait-time: 600 - # Size settings size: # Some effects would increase/decrease size so the option decides whether they could ignore the limit restricted-size-range: true - # Competition settings competition: # Use redis for cross server data synchronization @@ -228,48 +385,38 @@ mechanics: server-group: default # Increase this value would allow you to use more placeholders like {4_player} {5_score} in sacrifice of some performance placeholder-limit: 3 - - # If a player could get multiple loots from fishing, should the loots spawn at the same time or have delays for each (tick) + # If a player could get multiple loots from fishing, should the loots spawn at the same time or have delay for each (measured in ticks) multiple-loot-spawn-delay: 4 + # Totem settings + totem: + # Is it allowed for different types of totems to take effect at the same time + allow-multiple-type: true + # Is it allowed for totems of the same type to take effect cumulatively + allow-same-type: false + # Enable fake bait casting animation + bait-animation: true # Other settings other-settings: # It's recommended to use MiniMessage format. If you insist on using legacy color code "&", enable the support below. # Disable this would improve performance legacy-color-code-support: true - - # Thread pool 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: 10 - # 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: 10 - # If a thread is idle for more than this attribute value, it will exit due to timeout - keepAliveTime: 30 - - # Event priority: MONITOR HIGHEST HIGH NORMAL LOW LOWEST + # Fishing event priority: MONITOR HIGHEST HIGH NORMAL LOW LOWEST event-priority: NORMAL - # Save the data from cache to file periodically to minimize the data loss if server crashes # -1 to disable data-saving-interval: 600 - # Log the consumption of time on data saving log-data-saving: true - # Lock player's data if a player is playing on a server that connected to database # If you can ensure low database link latency and fast processing, you can consider disabling this option to improve performance lock-data: true - # Requires PlaceholderAPI to work placeholder-register: - '{record}': '%fishingstats_size-record_{loot}%' # Requires server expansion '{date}': '%server_time_yyyy-MM-dd-HH:mm:ss%' # Requires player expansion '{yaw}': '%player_yaw%' - # CustomFishing supports using items/blocks from other plugins # If items share the same id, they would inherit the effects # Check the wiki for examples @@ -278,13 +425,11 @@ other-settings: - vanilla block-detection-order: - vanilla - # Custom durability format custom-durability-format: - '' - 'Durability: {dur} / {max}' - - # Offset characters' unicodes + # Offset characters # Never edit this unless you know what you are doing offset-characters: font: customfishing:offset_chars diff --git a/plugin/src/main/resources/contents/bait/default.yml b/core/src/main/resources/contents/bait/default.yml similarity index 100% rename from plugin/src/main/resources/contents/bait/default.yml rename to core/src/main/resources/contents/bait/default.yml diff --git a/plugin/src/main/resources/contents/block/default.yml b/core/src/main/resources/contents/block/default.yml similarity index 100% rename from plugin/src/main/resources/contents/block/default.yml rename to core/src/main/resources/contents/block/default.yml diff --git a/plugin/src/main/resources/contents/category/default.yml b/core/src/main/resources/contents/category/default.yml similarity index 100% rename from plugin/src/main/resources/contents/category/default.yml rename to core/src/main/resources/contents/category/default.yml diff --git a/plugin/src/main/resources/contents/competition/default.yml b/core/src/main/resources/contents/competition/default.yml similarity index 100% rename from plugin/src/main/resources/contents/competition/default.yml rename to core/src/main/resources/contents/competition/default.yml diff --git a/plugin/src/main/resources/contents/enchant/default.yml b/core/src/main/resources/contents/enchant/default.yml similarity index 51% rename from plugin/src/main/resources/contents/enchant/default.yml rename to core/src/main/resources/contents/enchant/default.yml index d41bf12e..50136281 100644 --- a/plugin/src/main/resources/contents/enchant/default.yml +++ b/core/src/main/resources/contents/enchant/default.yml @@ -29,4 +29,43 @@ minecraft:luck_of_the_sea:3: type: group-mod value: - silver_star:+6 - - golden_star:+3 \ No newline at end of file + - golden_star:+3 +minecraft:lure:1: + requirements: {} + effects: + effect_1: + type: conditional + conditions: + "||": + in-lava: true + in-void: true + effects: + effect_1: + type: wait-time + value: -80 +minecraft:lure:2: + requirements: {} + effects: + effect_1: + type: conditional + conditions: + "||": + in-lava: true + in-void: true + effects: + effect_1: + type: wait-time + value: -160 +minecraft:lure:3: + requirements: {} + effects: + effect_1: + type: conditional + conditions: + "||": + in-lava: true + in-void: true + effects: + effect_1: + type: wait-time + value: -240 \ No newline at end of file diff --git a/plugin/src/main/resources/contents/entity/default.yml b/core/src/main/resources/contents/entity/default.yml similarity index 100% rename from plugin/src/main/resources/contents/entity/default.yml rename to core/src/main/resources/contents/entity/default.yml diff --git a/plugin/src/main/resources/contents/hook/default.yml b/core/src/main/resources/contents/hook/default.yml similarity index 100% rename from plugin/src/main/resources/contents/hook/default.yml rename to core/src/main/resources/contents/hook/default.yml diff --git a/plugin/src/main/resources/contents/item/default.yml b/core/src/main/resources/contents/item/default.yml similarity index 83% rename from plugin/src/main/resources/contents/item/default.yml rename to core/src/main/resources/contents/item/default.yml index dde3b933..932c585e 100644 --- a/plugin/src/main/resources/contents/item/default.yml +++ b/core/src/main/resources/contents/item/default.yml @@ -12,6 +12,12 @@ vanilla: disable-stat: true group: - river + events: + success: + action_mending: + type: mending + value: 1~7 + chance: 1.0 # Some rubbish stick: tag: false @@ -129,106 +135,95 @@ rainbow_fish: price: base: 100 # Enchantments -sharpness_book: +enchantment_book: tag: false material: enchanted_book group: enchantments nick: "" show-in-fishfinder: false disable-stat: true - random-stored-enchantments: - lv5: - enchant: minecraft:sharpness - level: 5 - chance: 0.1 - lv4: - enchant: minecraft:sharpness - level: 4 - chance: 0.2 - lv3: - enchant: minecraft:sharpness - level: 3 - chance: 0.4 - lv2: - enchant: minecraft:sharpness - level: 2 - chance: 0.7 - lv1: - enchant: minecraft:sharpness - level: 1 - chance: 1 -efficiency_book: - tag: false - material: enchanted_book - group: enchantments - show-in-fishfinder: false - nick: "" - disable-stat: true - random-stored-enchantments: - lv5: - enchant: minecraft:efficiency - level: 5 - chance: 0.1 - lv4: - enchant: minecraft:efficiency - level: 4 - chance: 0.2 - lv3: - enchant: minecraft:efficiency - level: 3 - chance: 0.4 - lv2: - enchant: minecraft:efficiency - level: 2 - chance: 0.7 - lv1: - enchant: minecraft:efficiency - level: 1 - chance: 1 -unbreaking_book: - tag: false - material: enchanted_book - group: enchantments - show-in-fishfinder: false - nick: "" - disable-stat: true - random-stored-enchantments: - lv3: - enchant: minecraft:unbreaking - level: 3 - chance: 0.2 - lv2: - enchant: minecraft:unbreaking - level: 2 - chance: 0.5 - lv1: - enchant: minecraft:unbreaking - level: 1 - chance: 1 -protection_book: - tag: false - material: enchanted_book - group: enchantments - show-in-fishfinder: false - nick: "" - disable-stat: true - random-stored-enchantments: - lv4: - enchant: minecraft:protection - level: 4 - chance: 0.1 - lv3: - enchant: minecraft:protection - level: 3 - chance: 0.2 - lv2: - enchant: minecraft:protection - level: 2 - chance: 0.5 - lv1: - enchant: minecraft:protection - level: 1 - chance: 1 + stored-enchantment-pool: + # max enchantments on the item + amount: + 1: 6 + 2: 3 + 3: 1 + pool: + 'minecraft:unbreaking:1': 6 + 'minecraft:unbreaking:2': 3 + 'minecraft:unbreaking:3': 1 + 'minecraft:sharpness:1': 6 + 'minecraft:sharpness:2': 3 + 'minecraft:sharpness:3': 2 + 'minecraft:sharpness:4': 1 + 'minecraft:efficiency:1': 12 + 'minecraft:efficiency:2': 6 + 'minecraft:efficiency:3': 2 + 'minecraft:efficiency:4': 1 + 'minecraft:protection:1': 10 + 'minecraft:protection:2': 6 + 'minecraft:protection:3': 2 + 'minecraft:feather_falling:1': 4 + 'minecraft:feather_falling:2': 3 + 'minecraft:feather_falling:3': 2 + 'minecraft:blast_protection:1': 10 + 'minecraft:blast_protection:2': 6 + 'minecraft:blast_protection:3': 2 + 'minecraft:fire_protection:1': 10 + 'minecraft:fire_protection:2': 6 + 'minecraft:fire_protection:3': 2 + 'minecraft:projectile_protection:1': 10 + 'minecraft:projectile_protection:2': 6 + 'minecraft:projectile_protection:3': 2 + 'minecraft:binding_curse:1': 2 + 'minecraft:vanishing_curse:1': 2 + 'minecraft:depth_strider:1': 8 + 'minecraft:depth_strider:2': 4 + 'minecraft:depth_strider:3': 2 + 'minecraft:fortune:1': 4 + 'minecraft:fortune:2': 3 + 'minecraft:fortune:3': 2 + 'minecraft:flame:1': 12 + 'minecraft:loyalty:1': 8 + 'minecraft:loyalty:2': 5 + 'minecraft:lure:1': 9 + 'minecraft:lure:2': 3 + 'minecraft:lure:3': 1 + 'minecraft:luck_of_the_sea:1': 8 + 'minecraft:luck_of_the_sea:2': 2 + 'minecraft:luck_of_the_sea:3': 1 + 'minecraft:channeling:1': 7 + 'minecraft:frost_walker:1': 7 + 'minecraft:mending:1': 10 + 'minecraft:power:1': 12 + 'minecraft:power:2': 6 + 'minecraft:power:3': 3 + 'minecraft:power:4': 1 + 'minecraft:knockback:1': 8 + 'minecraft:knockback:2': 3 + 'minecraft:silk_touch:1': 12 + 'minecraft:smite:1': 12 + 'minecraft:smite:2': 6 + 'minecraft:smite:3': 3 + 'minecraft:smite:4': 1 + 'minecraft:bane_of_arthropods:1': 12 + 'minecraft:bane_of_arthropods:2': 6 + 'minecraft:bane_of_arthropods:3': 3 + 'minecraft:bane_of_arthropods:4': 1 + 'minecraft:multishot:1': 7 + 'minecraft:punch:1': 8 + 'minecraft:punch:2': 4 + 'minecraft:thorns:1': 8 + 'minecraft:thorns:2': 4 + 'minecraft:looting:1': 12 + 'minecraft:looting:2': 6 + 'minecraft:looting:3': 2 + 'minecraft:soul_speed:1': 8 + 'minecraft:soul_speed:2': 4 + 'minecraft:fire_aspect:1': 10 + 'minecraft:fire_aspect:2': 5 + 'minecraft:quick_charge:1': 9 + 'minecraft:infinity:1': 7 # Fish tuna_fish: material: cod @@ -236,7 +231,7 @@ tuna_fish: name: Tuna Fish lore: - Tuna is a kind of healthy food. - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50001 group: ocean events: @@ -257,7 +252,7 @@ tuna_fish_silver_star: Star) lore: - Tuna is a kind of healthy food. - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50002 group: - silver_star @@ -280,7 +275,7 @@ tuna_fish_golden_star: Star) lore: - Tuna is a kind of healthy food. - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50003 group: - golden_star @@ -304,7 +299,7 @@ pike_fish: - of salt and fresh water. It can - survive in seawater, brackish fresh - water and inland freshwater lakes - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50004 group: ocean events: @@ -328,7 +323,7 @@ pike_fish_silver_star: - of salt and fresh water. It can - survive in seawater, brackish fresh - water and inland freshwater lakes - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50005 group: - silver_star @@ -354,7 +349,7 @@ pike_fish_golden_star: - of salt and fresh water. It can - survive in seawater, brackish fresh - water and inland freshwater lakes - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50006 group: - golden_star @@ -377,7 +372,7 @@ gold_fish: - Goldfish is one of most famous ornamental - fishes in the world. It originated in China - and has a history of more than 1700 years - - 'size: {size}cm' + - 'size: {size_formatted}cm' price: base: 70 bonus: 2.6 @@ -400,7 +395,7 @@ gold_fish_silver_star: - Goldfish is one of most famous ornamental - fishes in the world. It originated in China - and has a history of more than 1700 years - - 'size: {size}cm' + - 'size: {size_formatted}cm' price: base: 80 bonus: 3 @@ -425,7 +420,7 @@ gold_fish_golden_star: - Goldfish is one of most famous ornamental - fishes in the world. It originated in China - and has a history of more than 1700 years - - 'size: {size}cm' + - 'size: {size_formatted}cm' price: base: 100 bonus: 3.4 @@ -447,7 +442,7 @@ perch_fish: lore: - Living in various habitats and - foraging at dusk and early morning - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50010 group: river events: @@ -469,7 +464,7 @@ perch_fish_silver_star: lore: - Living in various habitats and - foraging at dusk and early morning - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50011 group: - silver_star @@ -493,7 +488,7 @@ perch_fish_golden_star: lore: - Living in various habitats and - foraging at dusk and early morning - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50012 group: - golden_star @@ -515,7 +510,7 @@ mullet_fish: lore: - Used in traditional Chinese medicine - to treat spleen and stomach weakness - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50013 group: river events: @@ -537,7 +532,7 @@ mullet_fish_silver_star: lore: - Used in traditional Chinese medicine - to treat spleen and stomach weakness - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50014 group: - silver_star @@ -561,7 +556,7 @@ mullet_fish_golden_star: lore: - Used in traditional Chinese medicine - to treat spleen and stomach weakness - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50015 group: - golden_star @@ -583,7 +578,7 @@ sardine_fish: lore: - Sardine fish is rich in DHA which improves memory - Therefore, sardine are also called "smart food" - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50016 group: ocean events: @@ -604,7 +599,7 @@ sardine_fish_silver_star: lore: - Sardine fish is rich in DHA which improves memory - Therefore, sardine are also called "smart food" - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50017 group: - silver_star @@ -627,7 +622,7 @@ sardine_fish_golden_star: lore: - Sardine fish is rich in DHA which improves memory - Therefore, sardine are also called "smart food" - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50018 group: - golden_star @@ -648,7 +643,7 @@ carp_fish: name: Carp Fish lore: - One of the most common edible fish - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50019 group: river events: @@ -668,7 +663,7 @@ carp_fish_silver_star: name: Carp Fish <#F5F5F5>(Silver Star) lore: - One of the most common edible fish - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50020 group: - silver_star @@ -690,7 +685,7 @@ carp_fish_golden_star: name: Carp Fish <#FFD700>(Golden Star) lore: - One of the most common edible fish - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50021 group: - golden_star @@ -712,7 +707,7 @@ cat_fish: lore: - Catfish is a fierce carnivorous fish with - sharp jaw teeth, short intestine and stomach - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50022 group: river events: @@ -733,7 +728,7 @@ cat_fish_silver_star: lore: - Catfish is a fierce carnivorous fish with - sharp jaw teeth, short intestine and stomach - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50023 group: - silver_star @@ -756,7 +751,7 @@ cat_fish_golden_star: lore: - Catfish is a fierce carnivorous fish with - sharp jaw teeth, short intestine and stomach - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50024 group: - golden_star @@ -778,7 +773,7 @@ octopus: lore: - Octopus is crazy about all kinds of utensils - People often use pots to catch octopus - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50025 group: ocean events: @@ -799,7 +794,7 @@ octopus_silver_star: lore: - Octopus is crazy about all kinds of utensils - People often use pots to catch octopus - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50026 group: - silver_star @@ -822,7 +817,7 @@ octopus_golden_star: lore: - Octopus is crazy about all kinds of utensils - People often use pots to catch octopus - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50027 group: - golden_star @@ -843,7 +838,7 @@ sunfish: name: <#F5DEB3>Sunfish lore: - It only has one huge head - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50028 group: ocean events: @@ -863,7 +858,7 @@ sunfish_silver_star: name: <#F5DEB3>Sunfish <#F5F5F5>(Silver Star) lore: - It only has one huge head - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50029 group: - silver_star @@ -885,7 +880,7 @@ sunfish_golden_star: name: <#F5DEB3>Sunfish <#FFD700>(Golden Star) lore: - It only has one huge head - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50030 group: - golden_star @@ -907,7 +902,7 @@ red_snapper_fish: lore: - They usually have a large family of ten or twenty - with a male as the "head of the family" - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50031 group: ocean events: @@ -929,7 +924,7 @@ red_snapper_fish_silver_star: lore: - They usually have a large family of ten or twenty - with a male as the "head of the family" - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50032 group: - silver_star @@ -953,7 +948,7 @@ red_snapper_fish_golden_star: lore: - They usually have a large family of ten or twenty - with a male as the "head of the family" - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50033 group: - golden_star @@ -975,7 +970,7 @@ salmon_void_fish: lore: - A fish from the hell - It's looking at you... - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50034 group: lava events: @@ -996,7 +991,7 @@ salmon_void_fish_silver_star: lore: - A fish from the hell - It's looking at you... - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50035 group: - silver_star @@ -1019,7 +1014,7 @@ salmon_void_fish_golden_star: lore: - A fish from the hell - It's looking at you... - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50036 group: - golden_star @@ -1041,7 +1036,7 @@ woodskip_fish: lore: - A very sensitive fish that can only - live in pools deep in the forest - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50037 group: river events: @@ -1062,7 +1057,7 @@ woodskip_fish_silver_star: lore: - A very sensitive fish that can only - live in pools deep in the forest - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50038 group: - silver_star @@ -1085,7 +1080,7 @@ woodskip_fish_golden_star: lore: - A very sensitive fish that can only - live in pools deep in the forest - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50039 group: - golden_star @@ -1107,7 +1102,7 @@ sturgeon_fish: lore: - An ancient bottom-feeder with a dwindling - population. Females can live up to 150 years - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50040 group: river events: @@ -1128,7 +1123,7 @@ sturgeon_fish_silver_star: lore: - An ancient bottom-feeder with a dwindling - population. Females can live up to 150 years - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50041 group: - silver_star @@ -1151,7 +1146,7 @@ sturgeon_fish_golden_star: lore: - An ancient bottom-feeder with a dwindling - population. Females can live up to 150 years - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50042 group: - golden_star @@ -1172,7 +1167,7 @@ blue_jellyfish: name: <#87CEFA>Jellyfish lore: - Looks like a blue umbrella - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50043 group: ocean events: @@ -1192,7 +1187,7 @@ blue_jellyfish_silver_star: name: <#87CEFA>Jellyfish lore: - Looks like a blue umbrella - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50044 group: - silver_star @@ -1214,7 +1209,7 @@ blue_jellyfish_golden_star: name: <#87CEFA>Jellyfish lore: - Looks like a blue umbrella - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50045 group: - golden_star @@ -1235,7 +1230,7 @@ pink_jellyfish: name: <#FFC0CB>Jellyfish lore: - Seems to be sweet - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50046 group: ocean events: @@ -1255,7 +1250,7 @@ pink_jellyfish_silver_star: name: <#FFC0CB>Jellyfish lore: - Seems to be sweet - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50047 group: - silver_star @@ -1277,7 +1272,7 @@ pink_jellyfish_golden_star: name: <#FFC0CB>Jellyfish lore: - Seems to be sweet - - 'size: {size}cm' + - 'size: {size_formatted}cm' custom-model-data: 50048 group: - golden_star diff --git a/plugin/src/main/resources/contents/minigame/default.yml b/core/src/main/resources/contents/minigame/default.yml similarity index 99% rename from plugin/src/main/resources/contents/minigame/default.yml rename to core/src/main/resources/contents/minigame/default.yml index 9ccb9d75..5d507b45 100644 --- a/plugin/src/main/resources/contents/minigame/default.yml +++ b/core/src/main/resources/contents/minigame/default.yml @@ -1417,7 +1417,7 @@ tension_game_easy: game-type: tension difficulty: 20~35 time: 30 - title: '{tension}' + title: '{progress}' # Tip would show on the title to guide the player how to play tip: 'Press to start' subtitle: @@ -1454,7 +1454,7 @@ tension_game_normal: game-type: tension difficulty: 35~50 time: 30 - title: '{tension}' + title: '{progress}' # Tip would show on the title to guide the player how to play tip: 'Press to start' subtitle: @@ -1491,7 +1491,7 @@ tension_game_hard: game-type: tension difficulty: 50~65 time: 30 - title: '{tension}' + title: '{progress}' # Tip would show on the title to guide the player how to play tip: 'Press to start' subtitle: diff --git a/plugin/src/main/resources/contents/rod/default.yml b/core/src/main/resources/contents/rod/default.yml similarity index 95% rename from plugin/src/main/resources/contents/rod/default.yml rename to core/src/main/resources/contents/rod/default.yml index 27c9c8fa..bf0c4184 100644 --- a/plugin/src/main/resources/contents/rod/default.yml +++ b/core/src/main/resources/contents/rod/default.yml @@ -26,14 +26,14 @@ beginner_rod: - ' - novice angler''s best friend.' - '' - '<#FFD700>Effects:' - - ' - Increase the hook time' + - ' - Increase the waiting time' - ' - Reduces the challenge of fishing' custom-model-data: 50001 max-durability: 64 effects: effect_1: type: wait-time-multiplier - value: 1.8 + value: 1.5 effect_2: type: difficulty value: -8 @@ -117,8 +117,8 @@ bone_rod: - ' - regular rods.' - '' - '<#FFD700>Effects:' - - ' - Fishing in lava' - - ' - Sometimes skeleton would grab the hook' + - ' - Fish in lava' + - ' - Attract skeletons' custom-model-data: 50005 max-durability: 32 effects: @@ -140,7 +140,7 @@ magical_rod: - '' - '<#FFD700>Effects:' - ' - Get an enchantment book from fishing.' - - ' - Require a long time to get hooked.' + - ' - The waiting time is very long.' - '' - '<#CD5C5C>Requirements:' - ' - 1x book bait' @@ -150,7 +150,7 @@ magical_rod: requirements: requirement_1: type: level - value: 10 + value: 1 not-met-actions: action_1: type: message @@ -169,7 +169,7 @@ magical_rod: cast: action_level: type: level - value: -10 + value: -1 effects: effect_1: type: group-mod-ignore-conditions @@ -193,7 +193,7 @@ master_rod: - ' - time it takes for a fish to bite.' - '' - '<#FFD700>Effects:' - - ' - Reduce the hook time' + - ' - Reduce the waiting time' - ' - Increase the challenge of fishing' - ' - Higher chance of getting quality fish' custom-model-data: 50007 diff --git a/plugin/src/main/resources/contents/totem/default.yml b/core/src/main/resources/contents/totem/default.yml similarity index 100% rename from plugin/src/main/resources/contents/totem/default.yml rename to core/src/main/resources/contents/totem/default.yml diff --git a/plugin/src/main/resources/contents/util/default.yml b/core/src/main/resources/contents/util/default.yml similarity index 98% rename from plugin/src/main/resources/contents/util/default.yml rename to core/src/main/resources/contents/util/default.yml index a4f3aa5a..54e78067 100644 --- a/plugin/src/main/resources/contents/util/default.yml +++ b/core/src/main/resources/contents/util/default.yml @@ -38,8 +38,7 @@ fishfinder: actions: action_1: type: fish-finder - value: false - + value: water water_effect: material: PAPER custom-model-data: 49998 diff --git a/plugin/src/main/resources/database.yml b/core/src/main/resources/database.yml similarity index 97% rename from plugin/src/main/resources/database.yml rename to core/src/main/resources/database.yml index af8f3715..1c133b46 100644 --- a/plugin/src/main/resources/database.yml +++ b/core/src/main/resources/database.yml @@ -1,3 +1,5 @@ +config-version: '${config_version}' + # file: # JSON # YAML diff --git a/plugin/src/main/resources/game-conditions.yml b/core/src/main/resources/game-conditions.yml similarity index 99% rename from plugin/src/main/resources/game-conditions.yml rename to core/src/main/resources/game-conditions.yml index 4b976d85..4ce82c76 100644 --- a/plugin/src/main/resources/game-conditions.yml +++ b/core/src/main/resources/game-conditions.yml @@ -5,7 +5,7 @@ global-group: sub-groups: lava_fishing_game: conditions: - lava-fishing: true + in-lava: true list: - hold_game_easy:+15 - hold_game_normal:+5 @@ -28,7 +28,7 @@ global-group: - hold_game_hard:+7 water_fish_game: conditions: - lava-fishing: false + in-water: true list: [] sub-groups: rainbow_fish_game: diff --git a/plugin/src/main/resources/loot-conditions.yml b/core/src/main/resources/loot-conditions.yml similarity index 98% rename from plugin/src/main/resources/loot-conditions.yml rename to core/src/main/resources/loot-conditions.yml index 3935c6cf..8c4b9475 100644 --- a/plugin/src/main/resources/loot-conditions.yml +++ b/core/src/main/resources/loot-conditions.yml @@ -5,7 +5,7 @@ global-group: sub-groups: loots_in_water: conditions: - lava-fishing: false + in-water: true environment: - normal '!rod': @@ -13,6 +13,7 @@ global-group: list: - rubbish:+15 - seagrass:+5 + - vanilla:+60 sub-groups: # parent ocean group ocean_fish: @@ -79,7 +80,6 @@ global-group: - minecraft:deep_lukewarm_ocean - minecraft:warm_ocean list: - - vanilla:+30 - rainbow_fish:+5 - stick:+15 - gold_fish:+15 @@ -126,7 +126,7 @@ global-group: - 'skeleton:+30' loots_in_lava: conditions: - lava-fishing: true + in-lava: true list: [] sub-groups: world_loots: diff --git a/plugin/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml similarity index 83% rename from plugin/src/main/resources/plugin.yml rename to core/src/main/resources/plugin.yml index 7df57363..62a1e5b3 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -1,11 +1,9 @@ name: CustomFishing -version: '${version}' -main: net.momirealms.customfishing.CustomFishingPluginImpl +version: '${project_version}' +main: net.momirealms.customfishing.bukkit.BukkitBootstrap api-version: 1.17 authors: [ XiaoMoMi ] folia-supported: true -depend: - - ProtocolLib softdepend: - Vault - PlaceholderAPI diff --git a/plugin/src/main/resources/schema/h2.sql b/core/src/main/resources/schema/h2.sql similarity index 100% rename from plugin/src/main/resources/schema/h2.sql rename to core/src/main/resources/schema/h2.sql diff --git a/plugin/src/main/resources/schema/mariadb.sql b/core/src/main/resources/schema/mariadb.sql similarity index 100% rename from plugin/src/main/resources/schema/mariadb.sql rename to core/src/main/resources/schema/mariadb.sql diff --git a/plugin/src/main/resources/schema/mysql.sql b/core/src/main/resources/schema/mysql.sql similarity index 100% rename from plugin/src/main/resources/schema/mysql.sql rename to core/src/main/resources/schema/mysql.sql diff --git a/plugin/src/main/resources/schema/sqlite.sql b/core/src/main/resources/schema/sqlite.sql similarity index 100% rename from plugin/src/main/resources/schema/sqlite.sql rename to core/src/main/resources/schema/sqlite.sql diff --git a/core/src/main/resources/translations/en.yml b/core/src/main/resources/translations/en.yml new file mode 100644 index 00000000..a64c0cc3 --- /dev/null +++ b/core/src/main/resources/translations/en.yml @@ -0,0 +1,90 @@ +# Don"t change this +config-version: "33" + +exception.invalid_syntax: "Invalid syntax. Correct syntax: " +exception.invalid_argument: "Invalid argument. Reason: " +exception.invalid_sender: " is not allowed to execute that command. Must be of type " +exception.unexpected: "An internal error occurred while attempting to perform this command" +exception.no_permission: "I'm sorry, but you do not have permission to perform this command" +exception.no_such_command: "Unknown command." +argument.entity.notfound.player: "" +argument.entity.notfound.entity: "" +argument.parse.failure.time: "'' is not a valid time format" +argument.parse.failure.material: "'' is not a valid material name" +argument.parse.failure.enchantment: "'' is not a valid enchantment" +argument.parse.failure.offlineplayer: "No player found for input ''" +argument.parse.failure.player: "No player found for input ''" +argument.parse.failure.world: "'' is not a valid Minecraft world" +argument.parse.failure.location.invalid_format: "'' is not a valid location. Required format is ' " +argument.parse.failure.location.mixed_local_absolute: "Cannot mix local and absolute coordinates. (either all coordinates use '^' or none do)" +argument.parse.failure.namespacedkey.namespace: "Invalid namespace ''. Must be [a-z0-9._-]" +argument.parse.failure.namespacedkey.key: "Invalid key ''. Must be [a-z0-9/._-]" +argument.parse.failure.namespacedkey.need_namespace: "Invalid input '', requires an explicit namespace" +argument.parse.failure.boolean: "Could not parse boolean from ''" +argument.parse.failure.number: "'' is not a valid number in the range to " +argument.parse.failure.char: "'' is not a valid character" +argument.parse.failure.string: "'' is not a valid string of type " +argument.parse.failure.uuid: "'' is not a valid UUID" +argument.parse.failure.enum: "'' is not one of the following: " +argument.parse.failure.regex: "'' does not match ''" +argument.parse.failure.flag.unknown: "Unknown flag ''" +argument.parse.failure.flag.duplicate_flag: "Duplicate flag ''" +argument.parse.failure.flag.no_flag_started: "No flag started. Don't know what to do with ''" +argument.parse.failure.flag.missing_argument: "Missing argument for ''" +argument.parse.failure.flag.no_permission: "You don't have permission to use ''" +argument.parse.failure.color: "'' is not a valid color" +argument.parse.failure.duration: "'' is not a duration format" +argument.parse.failure.aggregate.missing: "Missing component ''" +argument.parse.failure.aggregate.failure: "Invalid component '': " +argument.parse.failure.either: "Could not resolve or from ''" +argument.parse.failure.namedtextcolor: "'' is not a named text color" +command.reload.success: "Reloaded. Took ms." +command.item.failure.not_exist: "Item [] not exists." +command.item.give.success: "Successfully given x ." +command.item.get.success: "Successfully got x ." +command.item.import.failure.no_item: "You can't import air" +command.item.import.success: "Item [] has been saved to /plugins/CustomFishing/imported_items.yml" +command.fish_finder.possible_loots: "Possible loots here: " +command.fish_finder.no_loot: "No loot found here" +command.fish_finder.split_char: ", " +command.competition.failure.not_exist: "Competition does not exist." +command.competition.failure.no_competition: "There's no competition ongoing." +command.competition.stop.success: "Stopped the current competition." +command.competition.end.success: "Ended the current competition." +command.competition.start.success: "Started the competition." +command.bag.edit.failure.unsafe: "Cannot edit a player's fishing bag if they're active on another linked server." +command.bag.edit.failure.never_played: "The player hasn't joined the server before. Can't modify a nonexistent player's fishing bag." +command.bag.open.success: "Successfully opened the fishing bag for " +command.bag.open.failure.not_loaded: "Failed to open market for because data is not loaded" +command.data.failure.not_loaded: "Data hasn't been loaded. Please re-enter the server. If issues persist, reach out to the server admin." +command.market.open.success: "Successfully opened the market gui for " +command.market.open.failure.not_loaded: "Failed to open market for because data is not loaded" +command.data.unlock.success: "Successfully unlocked data for " +command.data.import.failure.not_exists: "That file doesn't exist" +command.data.import.failure.invalid_file: "That file is invalid" +command.data.import.failure.player_online: "Please kick all the online players before using this command" +command.data.import.start: "Importing..." +command.data.import.progress: "Progress: /" +command.data.import.success: "Successfully imported the data" +command.data.export.failure.player_online: "Please kick all the online players before using this command" +command.data.export.start: "Exporting..." +command.data.export.progress: "Progress: /" +command.data.export.success: "Successfully exported the data" +command.statistics.failure.not_loaded: "Data has not been loaded for that player" +command.statistics.failure.unsupported: "Unsupported operation" +command.statistics.modify.success: "Successfully modified the fishing statistics for " +command.statistics.reset.success: "Successfully reset the fishing statistics for " +command.statistics.query.size: "Max sizes: " +command.statistics.query.amount: "Amount of fish caught: " +competition.no_score: "No Score" +competition.no_player: "No Player" +competition.no_rank: "No Rank" +competition.goal.catch_amount: "Fish count caught" +competition.goal.max_size: "Largest fish caught" +competition.goal.min_size: "Smallest fish caught" +competition.goal.total_score: "Cumulative score of fish caught" +competition.goal.total_size: "Total length of fish caught" +format.day: "d" +format.hour: "h" +format.minute: "m" +format.second: "s" \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 7b26f395..33df6cc7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,53 @@ +# Project settings +# Rule: [major update].[feature update].[bug fix] +project_version=2.2.0 +config_version=33 +project_group=net.momirealms + +# Dependency settings +paper_version=1.20.4 +jetbrains_annotations_version=24.0.0 +slf4j_version=2.0.13 +log4j_version=2.23.1 +gson_version=2.10.1 +asm_version=9.7 +asm_commons_version=9.7 +jar_relocator_version=1.7 +h2_driver_version=2.2.224 +sqlite_driver_version=3.46.0.0 +adventure_bundle_version=4.17.0 +adventure_platform_version=4.3.3 +sparrow_heart_version=0.30 +cloud_core_version=2.0.0-rc.2 +cloud_services_version=2.0.0-rc.2 +cloud_brigadier_version=2.0.0-beta.9 +cloud_bukkit_version=2.0.0-beta.9 +cloud_paper_version=2.0.0-beta.9 +cloud_minecraft_extras_version=2.0.0-beta.9 +boosted_yaml_version=1.3.6 +byte_buddy_version=1.14.14 +mojang_brigadier_version=1.0.18 +mongodb_driver_version=5.1.0 +mariadb_driver_version=3.3.3 +mysql_driver_version=8.4.0 +hikari_version=5.1.0 +commons_pool_version=2.12.0 +bstats_version=3.0.2 +geantyref_version=1.3.15 +caffeine_version=3.1.8 +rtag_version=6290733498 +jedis_version=5.1.2 +exp4j_version=0.4.8 +placeholder_api_version=2.11.6 +invui_version=1.32 +vault_version=1.7 +guava_version=33.2.0-jre +lz4_version=1.8.0 + +# Proxy settings systemProp.socks.proxyHost=127.0.0.1 systemProp.socks.proxyPort=7890 - systemProp.http.proxyHost=127.0.0.1 systemProp.http.proxyPort=7890 - systemProp.https.proxyHost=127.0.0.1 systemProp.https.proxyPort=7890 \ No newline at end of file diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts deleted file mode 100644 index 93a55d56..00000000 --- a/plugin/build.gradle.kts +++ /dev/null @@ -1,110 +0,0 @@ -dependencies { - // server - compileOnly("dev.folia:folia-api:1.20.4-R0.1-SNAPSHOT") - - // packet - compileOnly("com.comphenix.protocol:ProtocolLib:5.1.0") - - // command - compileOnly("dev.jorel:commandapi-bukkit-core:9.4.1") - - // bStats - compileOnly("org.bstats:bstats-bukkit:3.0.2") - - // papi - compileOnly("me.clip:placeholderapi:2.11.5") - - // config - compileOnly("dev.dejvokep:boosted-yaml:1.3.4") - - // mythic - compileOnly("io.lumine:Mythic-Dist:5.3.5") - compileOnly("net.Indyuce:MMOItems-API:6.9.2-SNAPSHOT") - compileOnly("io.lumine:MythicLib-dist:1.6-SNAPSHOT") - compileOnly("net.Indyuce:MMOCore-API:1.12-SNAPSHOT") - - // Gson - compileOnly("com.google.code.gson:gson:2.10.1") - - // eco - compileOnly("com.willfp:eco:6.67.2") - compileOnly("com.willfp:EcoJobs:3.47.1") - compileOnly("com.willfp:EcoSkills:3.21.0") - compileOnly("com.willfp:libreforge:4.48.1") - - // database - compileOnly("org.xerial:sqlite-jdbc:3.45.3.0") - compileOnly("com.h2database:h2:2.2.224") - compileOnly("org.mongodb:mongodb-driver-sync:5.0.1") - compileOnly("com.zaxxer:HikariCP:5.0.1") - compileOnly("redis.clients:jedis:5.1.2") - - // others - compileOnly("com.github.LoneDev6:api-itemsadder:3.5.0c-r5") - compileOnly("io.th0rgal:oraxen:1.165.0") - compileOnly("pers.neige.neigeitems:NeigeItems:1.16.24") - compileOnly("com.github.Zrips:Jobs:4.17.2") - compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.21") - compileOnly("dev.aurelium:auraskills-api-bukkit:2.0.0-SNAPSHOT") - compileOnly("com.github.MilkBowl:VaultAPI:1.7") - compileOnly("org.betonquest:betonquest:2.0.0") - compileOnly("com.github.Xiao-MoMi:Custom-Crops:3.4.4.1") - compileOnly("org.apache.commons:commons-lang3:3.14.0") - - // local jars - compileOnly(files("libs/AdvancedEnchantments-api.jar")) - compileOnly(files("libs/BattlePass-4.0.6-api.jar")) - compileOnly(files("libs/RealisticSeasons-api.jar")) - compileOnly(files("libs/mcMMO-api.jar")) - compileOnly(files("libs/ClueScrolls-4.8.7-api.jar")) - compileOnly(files("libs/notquests-5.17.1.jar")) - compileOnly(files("libs/zaphkiel-2.0.24.jar")) - - // GUI - implementation("xyz.xenondevs.invui:invui:1.30") { - exclude("org.jetbrains", "annotations") - } - - // nbt - implementation("de.tr7zw:item-nbt-api:2.12.4") - - // api module - implementation(project(":api")) - - // sparrow heart - implementation("com.github.Xiao-MoMi:Sparrow-Heart:0.16") - - // adventure - implementation("net.kyori:adventure-api:4.17.0") - implementation("net.kyori:adventure-text-minimessage:4.17.0") - implementation("net.kyori:adventure-text-serializer-gson:4.17.0") { - exclude("com.google.code.gson", "gson") - } - implementation("net.kyori:adventure-platform-bukkit:4.3.2") -} - -tasks { - shadowJar { - exclude("org.jetbrains:annotations:*") - relocate ("org.apache.commons.pool2", "net.momirealms.customfishing.libraries.commonspool2") - relocate ("org.apache.commons.lang3", "net.momirealms.customfishing.libraries.lang3") - relocate ("com.mysql", "net.momirealms.customfishing.libraries.mysql") - relocate ("org.mariadb", "net.momirealms.customfishing.libraries.mariadb") - relocate ("com.zaxxer.hikari", "net.momirealms.customfishing.libraries.hikari") - relocate ("redis.clients.jedis", "net.momirealms.customfishing.libraries.jedis") - relocate ("com.mongodb", "net.momirealms.customfishing.libraries.mongodb") - relocate ("org.bson", "net.momirealms.customfishing.libraries.bson") - relocate ("net.objecthunter.exp4j", "net.momirealms.customfishing.libraries.exp4j") - relocate ("de.tr7zw.changeme", "net.momirealms.customfishing.libraries.changeme") - relocate ("net.kyori", "net.momirealms.customfishing.libraries") - relocate ("dev.jorel.commandapi", "net.momirealms.customfishing.libraries.commandapi") - relocate ("dev.dejvokep.boostedyaml", "net.momirealms.customfishing.libraries.boostedyaml") - relocate ("org.bstats", "net.momirealms.customfishing.libraries.bstats") - relocate ("net.momirealms.sparrow.heart", "net.momirealms.customfishing.libraries.heart") - relocate ("xyz.xenondevs", "net.momirealms.customfishing.libraries") - } -} - -tasks.withType { - options.encoding = "UTF-8" -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java b/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java deleted file mode 100644 index 5cb7bb1e..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.ProtocolLibrary; -import com.comphenix.protocol.ProtocolManager; -import com.comphenix.protocol.events.PacketContainer; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.event.CustomFishingReloadEvent; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.api.util.ReflectionUtils; -import net.momirealms.customfishing.command.CommandManagerImpl; -import net.momirealms.customfishing.compatibility.IntegrationManagerImpl; -import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; -import net.momirealms.customfishing.libraries.classpath.ReflectionClassPathAppender; -import net.momirealms.customfishing.libraries.dependencies.Dependency; -import net.momirealms.customfishing.libraries.dependencies.DependencyManager; -import net.momirealms.customfishing.libraries.dependencies.DependencyManagerImpl; -import net.momirealms.customfishing.mechanic.action.ActionManagerImpl; -import net.momirealms.customfishing.mechanic.bag.BagManagerImpl; -import net.momirealms.customfishing.mechanic.block.BlockManagerImpl; -import net.momirealms.customfishing.mechanic.competition.CompetitionManagerImpl; -import net.momirealms.customfishing.mechanic.effect.EffectManagerImpl; -import net.momirealms.customfishing.mechanic.entity.EntityManagerImpl; -import net.momirealms.customfishing.mechanic.fishing.FishingManagerImpl; -import net.momirealms.customfishing.mechanic.game.GameManagerImpl; -import net.momirealms.customfishing.mechanic.hook.HookManagerImpl; -import net.momirealms.customfishing.mechanic.item.ItemManagerImpl; -import net.momirealms.customfishing.mechanic.loot.LootManagerImpl; -import net.momirealms.customfishing.mechanic.market.MarketManagerImpl; -import net.momirealms.customfishing.mechanic.misc.ChatCatcherManager; -import net.momirealms.customfishing.mechanic.misc.CoolDownManager; -import net.momirealms.customfishing.mechanic.requirement.RequirementManagerImpl; -import net.momirealms.customfishing.mechanic.statistic.StatisticsManagerImpl; -import net.momirealms.customfishing.mechanic.totem.TotemManagerImpl; -import net.momirealms.customfishing.scheduler.SchedulerImpl; -import net.momirealms.customfishing.setting.CFConfig; -import net.momirealms.customfishing.setting.CFLocale; -import net.momirealms.customfishing.storage.StorageManagerImpl; -import net.momirealms.customfishing.util.NBTUtils; -import net.momirealms.customfishing.version.VersionManagerImpl; -import org.bstats.bukkit.Metrics; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class CustomFishingPluginImpl extends CustomFishingPlugin { - - private static ProtocolManager protocolManager; - private CoolDownManager coolDownManager; - private ChatCatcherManager chatCatcherManager; - private DependencyManager dependencyManager; - - public CustomFishingPluginImpl() { - super(); - } - - @Override - public void onLoad() { - this.versionManager = new VersionManagerImpl(this); - this.dependencyManager = new DependencyManagerImpl(this, new ReflectionClassPathAppender(this.getClassLoader())); - this.dependencyManager.loadDependencies(new ArrayList<>( - List.of( - Dependency.GSON, - Dependency.SLF4J_API, - Dependency.SLF4J_SIMPLE, - Dependency.BOOSTED_YAML, - Dependency.EXP4J, - Dependency.MYSQL_DRIVER, - Dependency.MARIADB_DRIVER, - Dependency.MONGODB_DRIVER_SYNC, - Dependency.MONGODB_DRIVER_CORE, - Dependency.MONGODB_DRIVER_BSON, - Dependency.JEDIS, - Dependency.COMMONS_POOL_2, - Dependency.COMMONS_LANG_3, - Dependency.H2_DRIVER, - Dependency.SQLITE_DRIVER, - Dependency.BSTATS_BASE, - Dependency.HIKARI, - Dependency.BSTATS_BUKKIT, - versionManager.isMojmap() ? Dependency.COMMAND_API_MOJMAP : Dependency.COMMAND_API - ) - )); - } - - @Override - public void onEnable() { - protocolManager = ProtocolLibrary.getProtocolManager(); - - NBTUtils.disableNBTAPILogs(); - ReflectionUtils.load(); - - this.actionManager = new ActionManagerImpl(this); - this.adventure = new AdventureHelper(this); - this.bagManager = new BagManagerImpl(this); - this.blockManager = new BlockManagerImpl(this); - this.commandManager = new CommandManagerImpl(this); - this.effectManager = new EffectManagerImpl(this); - this.fishingManager = new FishingManagerImpl(this); - this.gameManager = new GameManagerImpl(this); - this.itemManager = new ItemManagerImpl(this); - this.lootManager = new LootManagerImpl(this); - this.marketManager = new MarketManagerImpl(this); - this.entityManager = new EntityManagerImpl(this); - this.placeholderManager = new PlaceholderManagerImpl(this); - this.requirementManager = new RequirementManagerImpl(this); - this.scheduler = new SchedulerImpl(this); - this.storageManager = new StorageManagerImpl(this); - this.competitionManager = new CompetitionManagerImpl(this); - this.integrationManager = new IntegrationManagerImpl(this); - this.statisticsManager = new StatisticsManagerImpl(this); - this.coolDownManager = new CoolDownManager(this); - this.totemManager = new TotemManagerImpl(this); - this.hookManager = new HookManagerImpl(this); - this.chatCatcherManager = new ChatCatcherManager(this); - this.reload(); - super.initialized = true; - - if (CFConfig.metrics) new Metrics(this, 16648); - if (CFConfig.updateChecker) - this.versionManager.checkUpdate().thenAccept(result -> { - if (!result) this.getAdventure().sendConsoleMessage("[CustomFishing] You are using the latest version."); - else this.getAdventure().sendConsoleMessage("[CustomFishing] Update is available: https://polymart.org/resource/2723"); - }); - } - - @Override - public void onDisable() { - if (this.adventure != null) ((AdventureHelper) this.adventure).close(); - if (this.bagManager != null) ((BagManagerImpl) this.bagManager).disable(); - if (this.blockManager != null) ((BlockManagerImpl) this.blockManager).disable(); - if (this.effectManager != null) ((EffectManagerImpl) this.effectManager).disable(); - if (this.fishingManager != null) ((FishingManagerImpl) this.fishingManager).disable(); - if (this.gameManager != null) ((GameManagerImpl) this.gameManager).disable(); - if (this.itemManager != null) ((ItemManagerImpl) this.itemManager).disable(); - if (this.lootManager != null) ((LootManagerImpl) this.lootManager).disable(); - if (this.marketManager != null) ((MarketManagerImpl) this.marketManager).disable(); - if (this.entityManager != null) ((EntityManagerImpl) this.entityManager).disable(); - if (this.requirementManager != null) ((RequirementManagerImpl) this.requirementManager).disable(); - if (this.scheduler != null) ((SchedulerImpl) this.scheduler).shutdown(); - if (this.integrationManager != null) ((IntegrationManagerImpl) this.integrationManager).disable(); - if (this.competitionManager != null) ((CompetitionManagerImpl) this.competitionManager).disable(); - if (this.storageManager != null) ((StorageManagerImpl) this.storageManager).disable(); - if (this.placeholderManager != null) ((PlaceholderManagerImpl) this.placeholderManager).disable(); - if (this.statisticsManager != null) ((StatisticsManagerImpl) this.statisticsManager).disable(); - if (this.actionManager != null) ((ActionManagerImpl) this.actionManager).disable(); - if (this.totemManager != null) ((TotemManagerImpl) this.totemManager).disable(); - if (this.hookManager != null) ((HookManagerImpl) this.hookManager).disable(); - if (this.coolDownManager != null) this.coolDownManager.disable(); - if (this.chatCatcherManager != null) this.chatCatcherManager.disable(); - if (this.commandManager != null) this.commandManager.unload(); - HandlerList.unregisterAll(this); - } - - /** - * Reload the plugin - */ - @Override - public void reload() { - CFConfig.load(); - CFLocale.load(); - ((SchedulerImpl) this.scheduler).reload(); - ((RequirementManagerImpl) this.requirementManager).unload(); - ((RequirementManagerImpl) this.requirementManager).load(); - ((ActionManagerImpl) this.actionManager).unload(); - ((ActionManagerImpl) this.actionManager).load(); - ((GameManagerImpl) this.gameManager).unload(); - ((GameManagerImpl) this.gameManager).load(); - ((ItemManagerImpl) this.itemManager).unload(); - ((ItemManagerImpl) this.itemManager).load(); - ((LootManagerImpl) this.lootManager).unload(); - ((LootManagerImpl) this.lootManager).load(); - ((FishingManagerImpl) this.fishingManager).unload(); - ((FishingManagerImpl) this.fishingManager).load(); - ((TotemManagerImpl) this.totemManager).unload(); - ((TotemManagerImpl) this.totemManager).load(); - ((EffectManagerImpl) this.effectManager).unload(); - ((EffectManagerImpl) this.effectManager).load(); - ((MarketManagerImpl) this.marketManager).unload(); - ((MarketManagerImpl) this.marketManager).load(); - ((BagManagerImpl) this.bagManager).unload(); - ((BagManagerImpl) this.bagManager).load(); - ((BlockManagerImpl) this.blockManager).unload(); - ((BlockManagerImpl) this.blockManager).load(); - ((EntityManagerImpl) this.entityManager).unload(); - ((EntityManagerImpl) this.entityManager).load(); - ((CompetitionManagerImpl) this.competitionManager).unload(); - ((CompetitionManagerImpl) this.competitionManager).load(); - ((StorageManagerImpl) this.storageManager).reload(); - ((StatisticsManagerImpl) this.statisticsManager).unload(); - ((StatisticsManagerImpl) this.statisticsManager).load(); - ((PlaceholderManagerImpl) this.placeholderManager).unload(); - ((PlaceholderManagerImpl) this.placeholderManager).load(); - ((HookManagerImpl) this.hookManager).unload(); - ((HookManagerImpl) this.hookManager).load(); - this.commandManager.unload(); - this.commandManager.load(); - this.coolDownManager.unload(); - this.coolDownManager.load(); - this.chatCatcherManager.unload(); - this.chatCatcherManager.load(); - - CustomFishingReloadEvent event = new CustomFishingReloadEvent(this); - Bukkit.getPluginManager().callEvent(event); - } - - /** - * 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); - if (!config.exists()) this.saveResource(file, false); - 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); - } - - /** - * 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; - } - - /** - * Gets the ChatCatcherManager instance associated with the plugin. - * - * @return The ChatCatcherManager instance. - */ - public ChatCatcherManager getChatCatcherManager() { - return chatCatcherManager; - } - - public DependencyManager getDependencyManager() { - return dependencyManager; - } - - /** - * Retrieves the ProtocolManager instance used for managing packets. - * - * @return The ProtocolManager instance. - */ - @NotNull - public static ProtocolManager getProtocolManager() { - return protocolManager; - } - - public static void sendPacket(Player player, PacketContainer packet) { - protocolManager.sendServerPacket(player, packet); - } - - public static void sendPackets(Player player, PacketContainer... packets) { - List bundle = new ArrayList<>(Arrays.asList(packets)); - PacketContainer bundlePacket = new PacketContainer(PacketType.Play.Server.BUNDLE); - bundlePacket.getPacketBundles().write(0, bundle); - sendPacket(player, bundlePacket); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/adventure/AdventureHelper.java b/plugin/src/main/java/net/momirealms/customfishing/adventure/AdventureHelper.java deleted file mode 100644 index 9a0279bf..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/adventure/AdventureHelper.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.adventure; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.platform.bukkit.BukkitAudiences; -import net.kyori.adventure.sound.Sound; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.manager.AdventureManager; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.api.util.ReflectionUtils; -import net.momirealms.customfishing.setting.CFConfig; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; - -import java.lang.reflect.InvocationTargetException; - -public class AdventureHelper implements AdventureManager { - - private final BukkitAudiences adventure; - private static AdventureManager instance; - - public AdventureHelper(CustomFishingPlugin plugin) { - this.adventure = BukkitAudiences.create(plugin); - instance = this; - } - - public static AdventureManager getInstance() { - return instance; - } - - public void close() { - if (adventure != null) - adventure.close(); - } - - @Override - public Component getComponentFromMiniMessage(String text) { - if (text == null) { - return Component.empty(); - } - if (CFConfig.legacyColorSupport) { - return MiniMessage.miniMessage().deserialize(legacyToMiniMessage(text)); - } else { - return MiniMessage.miniMessage().deserialize(text); - } - } - - @Override - public void sendMessage(CommandSender sender, String s) { - if (s == null) return; - if (sender instanceof Player player) sendPlayerMessage(player, s); - else if (sender instanceof ConsoleCommandSender) sendConsoleMessage(s); - } - - @Override - public void sendMessageWithPrefix(CommandSender sender, String s) { - if (s == null) return; - if (sender instanceof Player player) sendPlayerMessage(player, CFLocale.MSG_Prefix + s); - else if (sender instanceof ConsoleCommandSender) sendConsoleMessage(CFLocale.MSG_Prefix + s); - } - - @Override - public void sendConsoleMessage(String s) { - if (s == null) return; - Audience au = adventure.sender(Bukkit.getConsoleSender()); - au.sendMessage(getComponentFromMiniMessage(s)); - } - - @Override - public void sendPlayerMessage(Player player, String s) { - if (s == null) return; - Audience au = adventure.player(player); - au.sendMessage(getComponentFromMiniMessage(s)); - } - - @Override - public void sendTitle(Player player, String title, String subtitle, int in, int duration, int out) { - sendTitle(player, getComponentFromMiniMessage(title), getComponentFromMiniMessage(subtitle), in, duration, out); - } - - @Override - public void sendTitle(Player player, Component title, Component subtitle, int in, int duration, int out) { - try { - PacketContainer titlePacket = new PacketContainer(PacketType.Play.Server.SET_TITLE_TEXT); - titlePacket.getModifier().write(0, getIChatComponent(componentToJson(title))); - PacketContainer subTitlePacket = new PacketContainer(PacketType.Play.Server.SET_SUBTITLE_TEXT); - subTitlePacket.getModifier().write(0, getIChatComponent(componentToJson(subtitle))); - PacketContainer timePacket = new PacketContainer(PacketType.Play.Server.SET_TITLES_ANIMATION); - timePacket.getIntegers().write(0, in); - timePacket.getIntegers().write(1, duration); - timePacket.getIntegers().write(2, out); - CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, titlePacket); - CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, subTitlePacket); - CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, timePacket); - } catch (InvocationTargetException | IllegalAccessException e) { - LogUtils.warn("Error occurred when sending title"); - } - } - - @Override - public void sendActionbar(Player player, String s) { - try { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.SET_ACTION_BAR_TEXT); - packet.getModifier().write(0, getIChatComponent(componentToJson(getComponentFromMiniMessage(s)))); - CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, packet); - } catch (InvocationTargetException | IllegalAccessException e) { - LogUtils.warn("Error occurred when sending actionbar"); - } - } - - @Override - public void sendSound(Player player, Sound.Source source, Key key, float volume, float pitch) { - Sound sound = Sound.sound(key, source, volume, pitch); - Audience au = adventure.player(player); - au.playSound(sound); - } - - @Override - public void sendSound(Player player, Sound sound) { - Audience au = adventure.player(player); - au.playSound(sound); - } - - @Override - public String legacyToMiniMessage(String legacy) { - StringBuilder stringBuilder = new StringBuilder(); - char[] chars = legacy.toCharArray(); - for (int i = 0; i < chars.length; i++) { - if (!isColorCode(chars[i])) { - stringBuilder.append(chars[i]); - continue; - } - if (i + 1 >= chars.length) { - stringBuilder.append(chars[i]); - continue; - } - switch (chars[i+1]) { - case '0' -> stringBuilder.append(""); - case '1' -> stringBuilder.append(""); - case '2' -> stringBuilder.append(""); - case '3' -> stringBuilder.append(""); - case '4' -> stringBuilder.append(""); - case '5' -> stringBuilder.append(""); - case '6' -> stringBuilder.append(""); - case '7' -> stringBuilder.append(""); - case '8' -> stringBuilder.append(""); - case '9' -> stringBuilder.append(""); - case 'a' -> stringBuilder.append(""); - case 'b' -> stringBuilder.append(""); - case 'c' -> stringBuilder.append(""); - case 'd' -> stringBuilder.append(""); - case 'e' -> stringBuilder.append(""); - case 'f' -> stringBuilder.append(""); - case 'r' -> stringBuilder.append(""); - case 'l' -> stringBuilder.append(""); - case 'm' -> stringBuilder.append(""); - case 'o' -> stringBuilder.append(""); - case 'n' -> stringBuilder.append(""); - case 'k' -> stringBuilder.append(""); - case 'x' -> { - if (i + 13 >= chars.length - || !isColorCode(chars[i+2]) - || !isColorCode(chars[i+4]) - || !isColorCode(chars[i+6]) - || !isColorCode(chars[i+8]) - || !isColorCode(chars[i+10]) - || !isColorCode(chars[i+12])) { - stringBuilder.append(chars[i]); - continue; - } - stringBuilder - .append("<#") - .append(chars[i+3]) - .append(chars[i+5]) - .append(chars[i+7]) - .append(chars[i+9]) - .append(chars[i+11]) - .append(chars[i+13]) - .append(">"); - i += 12; - } - default -> { - stringBuilder.append(chars[i]); - continue; - } - } - i++; - } - return stringBuilder.toString(); - } - - @Override - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public boolean isColorCode(char c) { - return c == '§' || c == '&'; - } - - @Override - public String componentToLegacy(Component component) { - return LegacyComponentSerializer.legacySection().serialize(component); - } - - @Override - public String componentToJson(Component component) { - return GsonComponentSerializer.gson().serialize(component); - } - - @Override - public Object shadedComponentToOriginalComponent(Component component) { - Object cp; - try { - cp = ReflectionUtils.gsonDeserializeMethod.invoke(ReflectionUtils.gsonInstance, GsonComponentSerializer.gson().serialize(component)); - } catch (InvocationTargetException | IllegalAccessException e) { - e.printStackTrace(); - return null; - } - return cp; - } - - public Object getIChatComponent(String json) throws InvocationTargetException, IllegalAccessException { - return ReflectionUtils.iChatComponentMethod.invoke(null, json); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/command/CommandManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/command/CommandManagerImpl.java deleted file mode 100644 index 7bbe1497..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/command/CommandManagerImpl.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.command; - -import dev.jorel.commandapi.CommandAPI; -import dev.jorel.commandapi.CommandAPIBukkitConfig; -import dev.jorel.commandapi.CommandAPICommand; -import dev.jorel.commandapi.arguments.EntitySelectorArgument; -import dev.jorel.commandapi.arguments.StringArgument; -import dev.jorel.commandapi.arguments.UUIDArgument; -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.manager.CommandManager; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.command.sub.*; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; - -import java.util.Collection; -import java.util.UUID; - -public class CommandManagerImpl implements CommandManager { - - private final CustomFishingPlugin plugin; - - public CommandManagerImpl(CustomFishingPluginImpl plugin) { - this.plugin = plugin; - } - - @Override - public void load() { - if (!CommandAPI.isLoaded()) - CommandAPI.onLoad(new CommandAPIBukkitConfig(plugin).silentLogs(true)); - new CommandAPICommand("customfishing") - .withAliases("cfishing") - .withPermission("customfishing.admin") - .withSubcommands( - getReloadCommand(), - getOpenCommand(), - getAboutCommand(), - GUIEditorCommand.INSTANCE.getEditorCommand(), - DataCommand.INSTANCE.getDataCommand(), - CompetitionCommand.INSTANCE.getCompetitionCommand(), - ItemCommand.INSTANCE.getItemCommand(), - DebugCommand.INSTANCE.getDebugCommand(), - StatisticsCommand.INSTANCE.getStatisticsCommand() - ) - .register(); - if (plugin.getMarketManager().isEnable()) { - new CommandAPICommand("sellfish") - .withPermission("customfishing.sellfish") - .executesPlayer((player, args) -> { - if (plugin.getMarketManager().isEnable()) - plugin.getMarketManager().openMarketGUI(player); - }) - .register(); - } - if (plugin.getBagManager().isEnabled()) { - FishingBagCommand.INSTANCE.getBagCommand().register(); - } - } - - @Override - public void unload() { - } - - private CommandAPICommand getReloadCommand() { - return new CommandAPICommand("reload") - .executes((sender, args) -> { - long time = System.currentTimeMillis(); - plugin.reload(); - AdventureHelper.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_Reload.replace("{time}", String.valueOf(System.currentTimeMillis()-time))); - }); - } - - @SuppressWarnings("unchecked") - private CommandAPICommand getOpenCommand() { - CommandAPICommand command = new CommandAPICommand("open"); - if (plugin.getMarketManager().isEnable()) { - command.withSubcommands( - new CommandAPICommand("market") - .withArguments(new EntitySelectorArgument.ManyPlayers("player")) - .withOptionalArguments(new StringArgument("-s")) - .executes((sender, args) -> { - Collection players = (Collection) args.get("player"); - assert players != null; - boolean silence = args.getOrDefault("-s","").equals("-s"); - for (Player player : players) { - plugin.getMarketManager().openMarketGUI(player); - if (!silence) AdventureHelper.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_Market_GUI_Open.replace("{player}", player.getName())); - } - }), - new CommandAPICommand("market-uuid") - .withArguments(new UUIDArgument("uuid")) - .withOptionalArguments(new StringArgument("-s")) - .executes((sender, args) -> { - UUID uuid = (UUID) args.get("uuid"); - Player player = Bukkit.getPlayer(uuid); - boolean silence = args.getOrDefault("-s","").equals("-s"); - if (player == null) return; - plugin.getMarketManager().openMarketGUI(player); - if (!silence) AdventureHelper.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_Market_GUI_Open.replace("{player}", player.getName())); - }) - ); - } - if (plugin.getBagManager().isEnabled()) { - command.withSubcommands( - new CommandAPICommand("bag") - .withArguments(new EntitySelectorArgument.ManyPlayers("player")) - .withOptionalArguments(new StringArgument("-s")) - .executes((sender, args) -> { - Collection players = (Collection) args.get("player"); - assert players != null; - boolean silence = args.getOrDefault("-s","").equals("-s"); - for (Player player : players) { - Inventory inventory = plugin.getBagManager().getOnlineBagInventory(player.getUniqueId()); - if (inventory != null) { - player.openInventory(inventory); - if (!silence) AdventureHelper.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_Fishing_Bag_Open.replace("{player}", player.getName())); - } else { - LogUtils.warn("Player " + player.getName() + "'s bag data has not been loaded."); - } - } - }), - new CommandAPICommand("bag-uuid") - .withArguments(new UUIDArgument("uuid")) - .withOptionalArguments(new StringArgument("-s")) - .executes((sender, args) -> { - UUID uuid = (UUID) args.get("uuid"); - Player player = Bukkit.getPlayer(uuid); - boolean silence = args.getOrDefault("-s","").equals("-s"); - if (player == null) return; - Inventory inventory = plugin.getBagManager().getOnlineBagInventory(uuid); - if (inventory != null) { - player.openInventory(inventory); - if (!silence) AdventureHelper.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_Fishing_Bag_Open.replace("{player}", player.getName())); - } else { - LogUtils.warn("Player " + player.getName() + "'s bag data has not been loaded."); - } - }) - ); - } - return command; - } - - private CommandAPICommand getAboutCommand() { - return new CommandAPICommand("about").executes((sender, args) -> { - AdventureHelper.getInstance().sendMessage(sender, "<#00BFFF>\uD83C\uDFA3 CustomFishing - <#87CEEB>" + CustomFishingPlugin.getInstance().getVersionManager().getPluginVersion()); - AdventureHelper.getInstance().sendMessage(sender, "<#B0C4DE>A fishing plugin that provides innovative mechanics and powerful loot system"); - AdventureHelper.getInstance().sendMessage(sender, "<#DA70D6>\uD83E\uDDEA Author: <#FFC0CB>XiaoMoMi"); - AdventureHelper.getInstance().sendMessage(sender, "<#FF7F50>\uD83D\uDD25 Contributors: <#FFA07A>0ft3n, <#FFA07A>Peng_Lx, <#FFA07A>Masaki, <#FFA07A>g2213swo"); - AdventureHelper.getInstance().sendMessage(sender, "<#FFD700>⭐ Document <#A9A9A9>| <#FAFAD2>⛏ Github <#A9A9A9>| <#48D1CC>\uD83D\uDD14 Polymart"); - }); - } -} 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 deleted file mode 100644 index 38bb4fac..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/command/sub/CompetitionCommand.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.command.sub; - -import dev.jorel.commandapi.CommandAPICommand; -import dev.jorel.commandapi.arguments.ArgumentSuggestions; -import dev.jorel.commandapi.arguments.StringArgument; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; -import net.momirealms.customfishing.setting.CFConfig; -import net.momirealms.customfishing.setting.CFLocale; -import net.momirealms.customfishing.storage.method.database.nosql.RedisManager; - -import java.util.Set; - -public class CompetitionCommand { - - public static CompetitionCommand INSTANCE = new CompetitionCommand(); - - public CommandAPICommand getCompetitionCommand() { - return new CommandAPICommand("competition") - .withSubcommands( - getCompetitionStartCommand(), - getCompetitionEndCommand(), - getCompetitionStopCommand() - ); - } - - private CommandAPICommand getCompetitionStartCommand() { - Set allCompetitions = CustomFishingPlugin.get().getCompetitionManager().getAllCompetitionKeys(); - var command = new CommandAPICommand("start") - .withArguments( - new StringArgument("id") - .replaceSuggestions( - ArgumentSuggestions.strings(allCompetitions) - ) - ); - if (CFConfig.redisRanking) command.withOptionalArguments(new StringArgument("server-group")); - command.executes((sender, args) -> { - String id = (String) args.get(0); - assert id != null; - if (!allCompetitions.contains(id)) { - AdventureHelper.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_Competition_Not_Exist.replace("{id}", id)); - return; - } - Object server = args.get("server-group"); - if (server != null) { - CustomFishingPlugin.get().getCompetitionManager().startCompetition(id, true, (String) server); - } else { - CustomFishingPlugin.get().getCompetitionManager().startCompetition(id, true, null); - } - }); - return command; - } - - private CommandAPICommand getCompetitionEndCommand() { - var command = new CommandAPICommand("end"); - if (CFConfig.redisRanking) command.withOptionalArguments(new StringArgument("server-group")); - command.executes((sender, args) -> { - Object server = args.get("server-group"); - if (server != null) { - RedisManager.getInstance().publishRedisMessage((String) server, "end"); - } else { - FishingCompetition competition = CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition(); - if (competition != null) { - CustomFishingPlugin.get().getScheduler().runTaskAsync(() -> competition.end(true)); - AdventureHelper.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_End_Competition); - } else { - AdventureHelper.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_No_Competition_Ongoing); - } - } - }); - return command; - } - - private CommandAPICommand getCompetitionStopCommand() { - var command = new CommandAPICommand("stop"); - if (CFConfig.redisRanking) command.withOptionalArguments(new StringArgument("server-group")); - command.executes((sender, args) -> { - Object server = args.get("server-group"); - if (server != null) { - RedisManager.getInstance().publishRedisMessage((String) server, "stop"); - } else { - FishingCompetition competition = CustomFishingPlugin.get().getCompetitionManager().getOnGoingCompetition(); - if (competition != null) { - CustomFishingPlugin.get().getScheduler().runTaskAsync(() -> { - competition.stop(true); - }); - AdventureHelper.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_Stop_Competition); - } else { - AdventureHelper.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_No_Competition_Ongoing); - } - } - }); - return command; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/command/sub/DataCommand.java b/plugin/src/main/java/net/momirealms/customfishing/command/sub/DataCommand.java deleted file mode 100644 index d2bfd6cc..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/command/sub/DataCommand.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.command.sub; - -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import dev.jorel.commandapi.CommandAPICommand; -import dev.jorel.commandapi.arguments.ArgumentSuggestions; -import dev.jorel.commandapi.arguments.StringArgument; -import dev.jorel.commandapi.arguments.UUIDArgument; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.DataStorageInterface; -import net.momirealms.customfishing.api.data.LegacyDataStorageInterface; -import net.momirealms.customfishing.api.data.PlayerData; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.storage.method.database.sql.MariaDBImpl; -import net.momirealms.customfishing.storage.method.database.sql.MySQLImpl; -import net.momirealms.customfishing.storage.method.file.YAMLImpl; -import net.momirealms.customfishing.util.CompletableFutures; -import org.bukkit.Bukkit; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -public class DataCommand { - - public static DataCommand INSTANCE = new DataCommand(); - - public CommandAPICommand getDataCommand() { - return new CommandAPICommand("data") - .withSubcommands( - getExportLegacyCommand(), - getExportCommand(), - getImportCommand(), - getUnlockCommand() - ); - } - - private CommandAPICommand getUnlockCommand() { - return new CommandAPICommand("unlock") - .withArguments(new UUIDArgument("uuid")) - .executes((sender, args) -> { - UUID uuid = (UUID) args.get("uuid"); - CustomFishingPlugin.get().getStorageManager().getDataSource().lockOrUnlockPlayerData(uuid, false); - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Successfully unlocked."); - }); - } - - @SuppressWarnings("DuplicatedCode") - private CommandAPICommand getExportLegacyCommand() { - return new CommandAPICommand("export-legacy") - .withArguments(new StringArgument("method") - .replaceSuggestions(ArgumentSuggestions.strings("MySQL", "MariaDB", "YAML"))) - .executes((sender, args) -> { - String arg = (String) args.get("method"); - if (arg == null) return; - CustomFishingPlugin plugin = CustomFishingPlugin.get(); - plugin.getScheduler().runTaskAsync(() -> { - - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Starting export."); - - LegacyDataStorageInterface dataStorageInterface; - switch (arg) { - case "MySQL" -> dataStorageInterface = new MySQLImpl(plugin); - case "MariaDB" -> dataStorageInterface = new MariaDBImpl(plugin); - case "YAML" -> dataStorageInterface = new YAMLImpl(plugin); - default -> { - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "No such legacy storage method."); - return; - } - } - - dataStorageInterface.initialize(); - Set uuids = dataStorageInterface.getUniqueUsers(true); - Set> futures = new HashSet<>(); - AtomicInteger userCount = new AtomicInteger(0); - Map out = Collections.synchronizedMap(new TreeMap<>()); - - for (UUID uuid : uuids) { - futures.add(dataStorageInterface.getLegacyPlayerData(uuid).thenAccept(it -> { - if (it.isPresent()) { - out.put(uuid, plugin.getStorageManager().toJson(it.get())); - userCount.incrementAndGet(); - } - })); - } - - CompletableFuture overallFuture = CompletableFutures.allOf(futures); - - while (true) { - try { - overallFuture.get(3, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - break; - } catch (TimeoutException e) { - LogUtils.info("Progress: " + userCount.get() + "/" + uuids.size()); - continue; - } - break; - } - - JsonObject outJson = new JsonObject(); - for (Map.Entry entry : out.entrySet()) { - outJson.addProperty(entry.getKey().toString(), entry.getValue()); - } - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm"); - String formattedDate = formatter.format(new Date()); - File outFile = new File(plugin.getDataFolder(), "exported-" + formattedDate + ".json.gz"); - try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(outFile.toPath())), StandardCharsets.UTF_8))) { - new GsonBuilder().disableHtmlEscaping().create().toJson(outJson, writer); - } catch (IOException e) { - e.printStackTrace(); - } - - dataStorageInterface.disable(); - - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Completed."); - }); - }); - } - - @SuppressWarnings("DuplicatedCode") - private CommandAPICommand getExportCommand() { - return new CommandAPICommand("export") - .executesConsole((sender, args) -> { - if (Bukkit.getOnlinePlayers().size() != 0) { - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Please kick all the players before exporting. Otherwise the cache will be inconsistent with data, resulting in the backup file not being up to date."); - return; - } - - CustomFishingPlugin plugin = CustomFishingPlugin.get(); - plugin.getScheduler().runTaskAsync(() -> { - - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Starting export."); - DataStorageInterface dataStorageInterface = plugin.getStorageManager().getDataSource(); - - Set uuids = dataStorageInterface.getUniqueUsers(false); - Set> futures = new HashSet<>(); - AtomicInteger userCount = new AtomicInteger(0); - Map out = Collections.synchronizedMap(new TreeMap<>()); - - int amount = uuids.size(); - for (UUID uuid : uuids) { - futures.add(dataStorageInterface.getPlayerData(uuid, false).thenAccept(it -> { - if (it.isPresent()) { - out.put(uuid, plugin.getStorageManager().toJson(it.get())); - userCount.incrementAndGet(); - } - })); - } - - CompletableFuture overallFuture = CompletableFutures.allOf(futures); - - while (true) { - try { - overallFuture.get(3, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - break; - } catch (TimeoutException e) { - LogUtils.info("Progress: " + userCount.get() + "/" + amount); - continue; - } - break; - } - - JsonObject outJson = new JsonObject(); - for (Map.Entry entry : out.entrySet()) { - outJson.addProperty(entry.getKey().toString(), entry.getValue()); - } - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm"); - String formattedDate = formatter.format(new Date()); - File outFile = new File(plugin.getDataFolder(), "exported-" + formattedDate + ".json.gz"); - try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(outFile.toPath())), StandardCharsets.UTF_8))) { - new GsonBuilder().disableHtmlEscaping().create().toJson(outJson, writer); - } catch (IOException e) { - e.printStackTrace(); - } - - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Completed."); - }); - }); - } - - @SuppressWarnings("DuplicatedCode") - private CommandAPICommand getImportCommand() { - return new CommandAPICommand("import") - .withArguments(new StringArgument("file")) - .executesConsole((sender, args) -> { - if (Bukkit.getOnlinePlayers().size() != 0) { - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Please kick all the players before importing. Otherwise the cache will be inconsistent with data."); - return; - } - - String fileName = (String) args.get("file"); - if (fileName == null) return; - CustomFishingPlugin plugin = CustomFishingPlugin.get(); - - File file = new File(plugin.getDataFolder(), fileName); - if (!file.exists()) { - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "File not exists."); - return; - } - if (!file.getName().endsWith(".json.gz")) { - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Invalid file."); - return; - } - - plugin.getScheduler().runTaskAsync(() -> { - - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Starting import."); - - JsonObject data; - try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(file.toPath())), StandardCharsets.UTF_8))) { - data = new GsonBuilder().disableHtmlEscaping().create().fromJson(reader, JsonObject.class); - } catch (IOException e) { - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Error occurred when reading the backup file."); - e.printStackTrace(); - return; - } - - DataStorageInterface dataStorageInterface = plugin.getStorageManager().getDataSource(); - var entrySet = data.entrySet(); - int amount = entrySet.size(); - AtomicInteger userCount = new AtomicInteger(0); - Set> futures = new HashSet<>(); - - for (Map.Entry entry : entrySet) { - UUID uuid = UUID.fromString(entry.getKey()); - if (entry.getValue() instanceof JsonPrimitive primitive) { - PlayerData playerData = plugin.getStorageManager().fromJson(primitive.getAsString()); - futures.add(dataStorageInterface.updateOrInsertPlayerData(uuid, playerData, true).thenAccept(it -> userCount.incrementAndGet())); - } - } - - CompletableFuture overallFuture = CompletableFutures.allOf(futures); - - while (true) { - try { - overallFuture.get(3, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - break; - } catch (TimeoutException e) { - LogUtils.info("Progress: " + userCount.get() + "/" + amount); - continue; - } - break; - } - - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Completed."); - }); - }); - } -} 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 deleted file mode 100644 index 29782ad2..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/command/sub/DebugCommand.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.command.sub; - -import de.tr7zw.changeme.nbtapi.NBTItem; -import dev.jorel.commandapi.CommandAPICommand; -import dev.jorel.commandapi.IStringTooltip; -import dev.jorel.commandapi.StringTooltip; -import dev.jorel.commandapi.arguments.ArgumentSuggestions; -import dev.jorel.commandapi.arguments.BooleanArgument; -import dev.jorel.commandapi.arguments.StringArgument; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.integration.SeasonInterface; -import net.momirealms.customfishing.api.manager.AdventureManager; -import net.momirealms.customfishing.api.mechanic.condition.FishingPreparation; -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 net.momirealms.customfishing.mechanic.fishing.FishingPreparationImpl; -import net.momirealms.customfishing.util.ConfigUtils; -import net.momirealms.customfishing.util.NBTUtils; -import net.momirealms.sparrow.heart.SparrowHeart; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.StringJoiner; - -public class DebugCommand { - - public static DebugCommand INSTANCE = new DebugCommand(); - - public CommandAPICommand getDebugCommand() { - return new CommandAPICommand("debug") - .withSubcommands( - getLootChanceCommand(), - getBiomeCommand(), - getSeasonCommand(), - getGroupCommand(), - getCategoryCommand(), - getNBTCommand(), - getLocationCommand() - ); - } - - public CommandAPICommand getBiomeCommand() { - return new CommandAPICommand("biome") - .executesPlayer((player, arg) -> { - AdventureHelper.getInstance().sendMessage(player, SparrowHeart.getInstance().getBiomeResourceLocation(player.getLocation())); - }); - } - - public CommandAPICommand getLocationCommand() { - return new CommandAPICommand("location") - .executesPlayer((player, arg) -> { - AdventureHelper.getInstance().sendMessage(player, player.getLocation().toString()); - }); - } - - public CommandAPICommand getNBTCommand() { - return new CommandAPICommand("nbt") - .executesPlayer((player, arg) -> { - ItemStack item = player.getInventory().getItemInMainHand(); - if (item.getType() == Material.AIR) - return; - ArrayList list = new ArrayList<>(); - ConfigUtils.mapToReadableStringList(NBTUtils.compoundToMap(new NBTItem(item)), list, 0, false); - for (String line : list) { - AdventureHelper.getInstance().sendMessage(player, line); - } - }); - } - - public CommandAPICommand getSeasonCommand() { - return new CommandAPICommand("season") - .executesPlayer((player, arg) -> { - SeasonInterface seasonInterface = CustomFishingPlugin.get().getIntegrationManager().getSeasonInterface(); - if (seasonInterface == null) { - AdventureHelper.getInstance().sendMessageWithPrefix(player, "NO SEASON PLUGIN"); - return; - } - AdventureHelper.getInstance().sendMessage(player, seasonInterface.getSeason(player.getLocation().getWorld())); - }); - } - - public CommandAPICommand getGroupCommand() { - return new CommandAPICommand("group") - .withArguments(new StringArgument("group")) - .executes((sender, arg) -> { - String group = (String) arg.get("group"); - StringJoiner stringJoiner = new StringJoiner(", "); - List groups = CustomFishingPlugin.get().getLootManager().getLootGroup(group); - if (groups != null) - for (String key : groups) { - stringJoiner.add(key); - } - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Group{" + group + "}[" + stringJoiner + "]"); - }); - } - - public CommandAPICommand getCategoryCommand() { - return new CommandAPICommand("category") - .withArguments(new StringArgument("category")) - .executes((sender, arg) -> { - String c = (String) arg.get("category"); - StringJoiner stringJoiner = new StringJoiner(", "); - List cs = CustomFishingPlugin.get().getStatisticsManager().getCategory(c); - if (cs != null) - for (String key : cs) { - stringJoiner.add(key); - } - AdventureHelper.getInstance().sendMessageWithPrefix(sender, "Category{" + c + "}[" + stringJoiner + "]"); - }); - } - - public CommandAPICommand getLootChanceCommand() { - return new CommandAPICommand("loot-chance") - .withArguments(new BooleanArgument("lava fishing").replaceSuggestions(ArgumentSuggestions.stringsWithTooltips(info -> - new IStringTooltip[] { - StringTooltip.ofString("true", "loots in lava"), - StringTooltip.ofString("false", "loots in water") - }))) - .executesPlayer((player, arg) -> { - if (player.getInventory().getItemInMainHand().getType() != Material.FISHING_ROD) { - AdventureHelper.getInstance().sendMessageWithPrefix(player, "Please hold a fishing rod before using this command."); - return; - } - FishingEffect initialEffect = CustomFishingPlugin.get().getEffectManager().getInitialEffect(); - FishingPreparation fishingPreparation = new FishingPreparationImpl(player, CustomFishingPlugin.get()); - boolean inLava = (boolean) arg.getOrDefault("lava fishing", false); - fishingPreparation.insertArg("{lava}", String.valueOf(inLava)); - fishingPreparation.mergeEffect(initialEffect); - EffectCarrier totemEffect = CustomFishingPlugin.get().getTotemManager().getTotemEffect(player.getLocation()); - if (totemEffect != null) - for (EffectModifier modifier : totemEffect.getEffectModifiers()) { - modifier.modify(initialEffect, fishingPreparation); - } - var map = CustomFishingPlugin.get().getLootManager().getPossibleLootKeysWithWeight(initialEffect, fishingPreparation); - List loots = new ArrayList<>(); - double sum = 0; - for (Map.Entry entry : map.entrySet()) { - double weight = entry.getValue(); - String loot = entry.getKey(); - if (weight <= 0) continue; - loots.add(new LootWithWeight(loot, weight)); - sum += weight; - } - LootWithWeight[] lootArray = loots.toArray(new LootWithWeight[0]); - quickSort(lootArray, 0,lootArray.length - 1); - AdventureManager adventureManager = AdventureHelper.getInstance(); - adventureManager.sendMessage(player, "---------- results ---------"); - for (LootWithWeight loot : lootArray) { - adventureManager.sendMessage(player, loot.key() + ": " + String.format("%.2f", loot.weight()*100/sum) + "% (" + String.format("%.2f", loot.weight()) + ")"); - } - adventureManager.sendMessage(player, "----------- end -----------"); - }); - } - - public record LootWithWeight(String key, double weight) { - } - - private static void quickSort(LootWithWeight[] loot, int low, int high) { - if (low < high) { - int pi = partition(loot, low, high); - quickSort(loot, low, pi - 1); - quickSort(loot, pi + 1, high); - } - } - - private static int partition(LootWithWeight[] loot, int low, int high) { - double pivot = loot[high].weight(); - int i = low - 1; - for (int j = low; j <= high - 1; j++) { - if (loot[j].weight() > pivot) { - i++; - swap(loot, i, j); - } - } - swap(loot, i + 1, high); - return i + 1; - } - - private static void swap(LootWithWeight[] loot, int i, int j) { - LootWithWeight temp = loot[i]; - loot[i] = loot[j]; - loot[j] = temp; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/command/sub/FishingBagCommand.java b/plugin/src/main/java/net/momirealms/customfishing/command/sub/FishingBagCommand.java deleted file mode 100644 index b147f3f5..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/command/sub/FishingBagCommand.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.command.sub; - -import dev.jorel.commandapi.CommandAPICommand; -import dev.jorel.commandapi.arguments.PlayerArgument; -import dev.jorel.commandapi.arguments.UUIDArgument; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.user.OfflineUser; -import net.momirealms.customfishing.setting.CFConfig; -import net.momirealms.customfishing.setting.CFLocale; -import net.momirealms.customfishing.storage.user.OfflineUserImpl; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; - -import java.util.UUID; - -public class FishingBagCommand { - - public static FishingBagCommand INSTANCE = new FishingBagCommand(); - - public CommandAPICommand getBagCommand() { - return new CommandAPICommand("fishingbag") - .withPermission("fishingbag.user") - .withSubcommands(getEditOnlineCommand(), getEditOfflineCommand()) - .executesPlayer(((player, args) -> { - if (CustomFishingPlugin.get().getBagManager().isEnabled()) { - var inv = CustomFishingPlugin.get().getBagManager().getOnlineBagInventory(player.getUniqueId()); - if (inv != null) { - player.openInventory(inv); - } else { - AdventureHelper.getInstance().sendMessageWithPrefix(player, CFLocale.MSG_Data_Not_Loaded); - } - } - })); - } - - private CommandAPICommand getEditOnlineCommand() { - return new CommandAPICommand("edit-online") - .withPermission("fishingbag.admin") - .withArguments(new PlayerArgument("player")) - .executesPlayer(((player, args) -> { - Player player1 = (Player) args.get("player"); - UUID uuid = player1.getUniqueId(); - Inventory onlineInv = CustomFishingPlugin.get().getBagManager().getOnlineBagInventory(uuid); - if (onlineInv != null) { - player.openInventory(onlineInv); - } - })); - } - - private CommandAPICommand getEditOfflineCommand() { - return new CommandAPICommand("edit-offline") - .withPermission("fishingbag.admin") - .withArguments(new UUIDArgument("UUID")) - .executesPlayer(((player, args) -> { - UUID uuid = (UUID) args.get("UUID"); - Player online = Bukkit.getPlayer(uuid); - if (online != null) { - Inventory onlineInv = CustomFishingPlugin.get().getBagManager().getOnlineBagInventory(uuid); - if (onlineInv != null) { - player.openInventory(onlineInv); - return; - } - } - CustomFishingPlugin.get().getStorageManager().getOfflineUser(uuid, CFConfig.lockData).thenAccept(optional -> { - if (optional.isEmpty()) { - AdventureHelper.getInstance().sendMessageWithPrefix(player, CFLocale.MSG_Never_Played); - return; - } - OfflineUser offlineUser = optional.get(); - if (offlineUser == OfflineUserImpl.LOCKED_USER) { - AdventureHelper.getInstance().sendMessageWithPrefix(player, CFLocale.MSG_Unsafe_Modification); - return; - } - CustomFishingPlugin.get().getScheduler().runTaskSync(() -> { - CustomFishingPlugin.get().getBagManager().editOfflinePlayerBag(player, offlineUser); - }, player.getLocation()); - }); - })); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/command/sub/GUIEditorCommand.java b/plugin/src/main/java/net/momirealms/customfishing/command/sub/GUIEditorCommand.java deleted file mode 100644 index 874d9b6d..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/command/sub/GUIEditorCommand.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.customfishing.command.sub; - -import dev.jorel.commandapi.CommandAPICommand; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.gui.page.file.FileSelector; - -import java.io.File; - -public class GUIEditorCommand { - - public static GUIEditorCommand INSTANCE = new GUIEditorCommand(); - - public CommandAPICommand getEditorCommand() { - return new CommandAPICommand("browser") - .executesPlayer((player, arg) -> { - new FileSelector(player, new File(CustomFishingPlugin.get().getDataFolder(), "contents")); - }); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/command/sub/ItemCommand.java b/plugin/src/main/java/net/momirealms/customfishing/command/sub/ItemCommand.java deleted file mode 100644 index 15eba24f..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/command/sub/ItemCommand.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.command.sub; - -import de.tr7zw.changeme.nbtapi.NBTItem; -import dev.jorel.commandapi.CommandAPICommand; -import dev.jorel.commandapi.arguments.ArgumentSuggestions; -import dev.jorel.commandapi.arguments.EntitySelectorArgument; -import dev.jorel.commandapi.arguments.IntegerArgument; -import dev.jorel.commandapi.arguments.StringArgument; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.common.Key; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.item.BuildableItem; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.setting.CFLocale; -import net.momirealms.customfishing.util.ItemUtils; -import net.momirealms.customfishing.util.NBTUtils; -import org.bukkit.Material; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import java.io.File; -import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -public class ItemCommand { - - public static ItemCommand INSTANCE = new ItemCommand(); - - private final HashMap completionMap = new HashMap<>(); - - public CommandAPICommand getItemCommand() { - return new CommandAPICommand("items") - .withSubcommands( - getSubCommand("item"), - getSubCommand("util"), - getSubCommand("bait"), - getSubCommand("rod"), - getSubCommand("hook") - ); - } - - private CommandAPICommand getSubCommand(String namespace) { - completionMap.put(namespace, CustomFishingPlugin.get() - .getItemManager() - .getAllItemsKey() - .stream() - .filter(it -> it.namespace().equals(namespace)) - .map(Key::value) - .toList().toArray(new String[0])); - return new CommandAPICommand(namespace) - .withSubcommands( - getCommand(namespace), - giveCommand(namespace), - importCommand(namespace) - ); - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private CommandAPICommand importCommand(String namespace) { - return new CommandAPICommand("import") - .withArguments(new StringArgument("key")) - .withOptionalArguments(new StringArgument("file")) - .executesPlayer((player, args) -> { - String key = (String) args.get("key"); - String fileName = args.getOrDefault("file","import") + ".yml"; - ItemStack itemStack = player.getInventory().getItemInMainHand(); - if (itemStack.getType() == Material.AIR) - return; - File file = new File(CustomFishingPlugin.get().getDataFolder(), - "contents" + File.separator + namespace + File.separator + fileName); - try { - if (!file.exists()) { - file.createNewFile(); - } - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - config.set(key + ".material", itemStack.getType().toString()); - config.set(key + ".amount", itemStack.getAmount()); - Map nbtMap = NBTUtils.compoundToMap(new NBTItem(itemStack)); - if (nbtMap.size() != 0) { - config.createSection(key + ".nbt", nbtMap); - } - try { - config.save(file); - AdventureHelper.getInstance().sendMessageWithPrefix(player, "Imported! Saved to " + file.getAbsolutePath()); - } catch (IOException e) { - e.printStackTrace(); - } - } catch (IOException e) { - LogUtils.warn("Failed to create imported file.", e); - } - }); - } - - private CommandAPICommand getCommand(String namespace) { - return new CommandAPICommand("get") - .withArguments(new StringArgument("id") - .replaceSuggestions(ArgumentSuggestions.strings( - info -> completionMap.get(namespace) - ))) - .withOptionalArguments(new IntegerArgument("amount", 1)) - .executesPlayer((player, args) -> { - String id = (String) args.get("id"); - assert id != null; - int amount = (int) args.getOrDefault("amount", 1); - ItemStack item = CustomFishingPlugin.get().getItemManager().build(player, namespace, id, new Condition(player).getArgs()); - if (item != null) { - int actual = ItemUtils.giveItem(player, item, amount); - AdventureHelper.getInstance().sendMessageWithPrefix(player, CFLocale.MSG_Get_Item.replace("{item}", id).replace("{amount}", String.valueOf(actual))); - } else { - AdventureHelper.getInstance().sendMessageWithPrefix(player, CFLocale.MSG_Item_Not_Exists); - } - }); - } - - @SuppressWarnings("unchecked") - private CommandAPICommand giveCommand(String namespace) { - return new CommandAPICommand("give") - .withArguments(new EntitySelectorArgument.ManyPlayers("player")) - .withArguments(new StringArgument("id") - .replaceSuggestions(ArgumentSuggestions.strings( - info -> completionMap.get(namespace) - ))) - .withOptionalArguments(new IntegerArgument("amount", 1)) - .withOptionalArguments(new StringArgument("-s")) - .executes((sender, args) -> { - Collection players = (Collection) args.get("player"); - String id = (String) args.get("id"); - boolean silence = args.getOrDefault("-s", "").equals("-s"); - int amount = (int) args.getOrDefault("amount", 1); - BuildableItem buildableItem = CustomFishingPlugin.get().getItemManager().getBuildableItem(namespace, id); - if (buildableItem != null) { - assert players != null; - for (Player player : players) { - ItemStack item = CustomFishingPlugin.get().getItemManager().build(player, namespace, id, new Condition(player).getArgs()); - int actual = ItemUtils.giveItem(player, item, amount); - if (!silence) AdventureHelper.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_Give_Item.replace("{item}", id).replace("{amount}", String.valueOf(actual)).replace("{player}", player.getName())); - } - } else { - AdventureHelper.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_Item_Not_Exists); - } - }); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/command/sub/StatisticsCommand.java b/plugin/src/main/java/net/momirealms/customfishing/command/sub/StatisticsCommand.java deleted file mode 100644 index f8d78dfb..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/command/sub/StatisticsCommand.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.command.sub; - -import dev.jorel.commandapi.CommandAPICommand; -import dev.jorel.commandapi.arguments.*; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.loot.Loot; -import net.momirealms.customfishing.api.mechanic.statistic.Statistics; -import net.momirealms.customfishing.api.util.LogUtils; -import org.bukkit.entity.Player; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; - -public class StatisticsCommand { - - public static StatisticsCommand INSTANCE = new StatisticsCommand(); - - private Collection loots = new HashSet<>(); - - public CommandAPICommand getStatisticsCommand() { - loots = CustomFishingPlugin.get().getLootManager().getAllLoots().stream().filter(it -> !it.disableStats()).map(Loot::getID).toList(); - return new CommandAPICommand("statistics") - .withSubcommands( - getSetCommand(), - getResetCommand(), - getQueryCommand(), - getAddCommand() - ); - } - - @SuppressWarnings("unchecked") - private CommandAPICommand getSetCommand() { - return new CommandAPICommand("set") - .withArguments(new EntitySelectorArgument.ManyPlayers("player")) - .withArguments(new StringArgument("id").replaceSuggestions(ArgumentSuggestions.strings(loots))) - .withArguments(new IntegerArgument("amount", 0)) - .executes((sender, args) -> { - Collection players = (Collection) args.get("player"); - String id = (String) args.get("id"); - int amount = (int) args.getOrDefault("amount", 0); - assert players != null; - Loot loot = CustomFishingPlugin.get().getLootManager().getLoot(id); - for (Player player : players) { - Statistics statistics = CustomFishingPlugin.get().getStatisticsManager().getStatistics(player.getUniqueId()); - if (statistics != null) { - if (loot != null) - statistics.setData(id, amount); - else - throw new RuntimeException("Loot " + id + " doesn't exist."); - } else { - LogUtils.warn("Player " + player.getName() + "'s statistics data has not been loaded."); - } - } - }); - } - - @SuppressWarnings("unchecked") - private CommandAPICommand getResetCommand() { - return new CommandAPICommand("reset") - .withArguments(new EntitySelectorArgument.ManyPlayers("player")) - .executes((sender, args) -> { - Collection players = (Collection) args.get("player"); - assert players != null; - for (Player player : players) { - Statistics statistics = CustomFishingPlugin.get().getStatisticsManager().getStatistics(player.getUniqueId()); - if (statistics != null) { - statistics.reset(); - } else { - LogUtils.warn("Player " + player.getName() + "'s statistics data has not been loaded."); - } - } - }); - } - - @SuppressWarnings("unchecked") - private CommandAPICommand getAddCommand() { - return new CommandAPICommand("add") - .withArguments(new EntitySelectorArgument.ManyPlayers("player")) - .withArguments(new StringArgument("id").replaceSuggestions(ArgumentSuggestions.strings(loots))) - .withArguments(new IntegerArgument("amount", 0)) - .executes((sender, args) -> { - Collection players = (Collection) args.get("player"); - String id = (String) args.get("id"); - int amount = (int) args.getOrDefault("amount", 0); - assert players != null; - Loot loot = CustomFishingPlugin.get().getLootManager().getLoot(id); - for (Player player : players) { - Statistics statistics = CustomFishingPlugin.get().getStatisticsManager().getStatistics(player.getUniqueId()); - if (statistics != null) { - if (loot != null) - statistics.addLootAmount(loot, new Condition(player), amount); - else - throw new RuntimeException("Loot " + id + " doesn't exist."); - } else { - LogUtils.warn("Player " + player.getName() + "'s statistics data has not been loaded."); - } - } - }); - } - - private CommandAPICommand getQueryCommand() { - return new CommandAPICommand("query") - .withArguments(new PlayerArgument("player")) - .executes((sender, args) -> { - Player player = (Player) args.get("player"); - assert player != null; - Statistics statistics = CustomFishingPlugin.get().getStatisticsManager().getStatistics(player.getUniqueId()); - if (statistics != null) { - var adventure = AdventureHelper.getInstance(); - for (Map.Entry entry : statistics.getStatisticMap().entrySet()) { - adventure.sendMessage(sender, entry.getKey() + ": " + entry.getValue()); - } - } else { - throw new RuntimeException("Player " + player.getName() + "'s statistics data has not been loaded."); - } - }); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/IntegrationManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/compatibility/IntegrationManagerImpl.java deleted file mode 100644 index 1879c881..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/IntegrationManagerImpl.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.compatibility; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.integration.EnchantmentInterface; -import net.momirealms.customfishing.api.integration.LevelInterface; -import net.momirealms.customfishing.api.integration.SeasonInterface; -import net.momirealms.customfishing.api.manager.IntegrationManager; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.compatibility.block.ItemsAdderBlockImpl; -import net.momirealms.customfishing.compatibility.enchant.AdvancedEnchantmentsImpl; -import net.momirealms.customfishing.compatibility.enchant.VanillaEnchantmentsImpl; -import net.momirealms.customfishing.compatibility.entity.ItemsAdderEntityImpl; -import net.momirealms.customfishing.compatibility.entity.MythicEntityImpl; -import net.momirealms.customfishing.compatibility.item.*; -import net.momirealms.customfishing.compatibility.level.*; -import net.momirealms.customfishing.compatibility.quest.BattlePassHook; -import net.momirealms.customfishing.compatibility.quest.BetonQuestHook; -import net.momirealms.customfishing.compatibility.quest.ClueScrollsHook; -import net.momirealms.customfishing.compatibility.season.CustomCropsSeasonImpl; -import net.momirealms.customfishing.compatibility.season.RealisticSeasonsImpl; -import org.bukkit.Bukkit; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -public class IntegrationManagerImpl implements IntegrationManager { - - private final CustomFishingPlugin plugin; - private final HashMap levelPluginMap; - private final HashMap enchantmentPluginMap; - private SeasonInterface seasonInterface; - - public IntegrationManagerImpl(CustomFishingPlugin plugin) { - this.plugin = plugin; - this.levelPluginMap = new HashMap<>(); - this.enchantmentPluginMap = new HashMap<>(); - this.load(); - } - - public void disable() { - this.enchantmentPluginMap.clear(); - this.levelPluginMap.clear(); - } - - public void load() { - if (Bukkit.getPluginManager().getPlugin("ItemsAdder") != null) { - plugin.getItemManager().registerItemLibrary(new ItemsAdderItemImpl()); - plugin.getBlockManager().registerBlockLibrary(new ItemsAdderBlockImpl()); - plugin.getEntityManager().registerEntityLibrary(new ItemsAdderEntityImpl()); - hookMessage("ItemsAdder"); - } - if (Bukkit.getPluginManager().getPlugin("MMOItems") != null) { - plugin.getItemManager().registerItemLibrary(new MMOItemsItemImpl()); - hookMessage("MMOItems"); - } - if (Bukkit.getPluginManager().getPlugin("Oraxen") != null) { - plugin.getItemManager().registerItemLibrary(new OraxenItemImpl()); - hookMessage("Oraxen"); - } - if (plugin.isHookedPluginEnabled("Zaphkiel")) { - plugin.getItemManager().registerItemLibrary(new ZaphkielItemImpl()); - hookMessage("Zaphkiel"); - } - if (plugin.isHookedPluginEnabled("NeigeItems")) { - plugin.getItemManager().registerItemLibrary(new NeigeItemsItemImpl()); - hookMessage("NeigeItems"); - } - if (Bukkit.getPluginManager().getPlugin("MythicMobs") != null) { - plugin.getItemManager().registerItemLibrary(new MythicMobsItemImpl()); - plugin.getEntityManager().registerEntityLibrary(new MythicEntityImpl()); - hookMessage("MythicMobs"); - } - if (plugin.isHookedPluginEnabled("EcoJobs")) { - registerLevelPlugin("EcoJobs", new EcoJobsImpl()); - hookMessage("EcoJobs"); - } - if (plugin.isHookedPluginEnabled("EcoSkills")) { - registerLevelPlugin("EcoSkills", new EcoSkillsImpl()); - hookMessage("EcoSkills"); - } - if (Bukkit.getPluginManager().getPlugin("Jobs") != null) { - registerLevelPlugin("JobsReborn", new JobsRebornImpl()); - hookMessage("JobsReborn"); - } - if (plugin.isHookedPluginEnabled("MMOCore")) { - registerLevelPlugin("MMOCore", new MMOCoreImpl()); - hookMessage("MMOCore"); - } - if (plugin.isHookedPluginEnabled("mcMMO")) { - try { - plugin.getItemManager().registerItemLibrary(new McMMOTreasureImpl()); - } catch (ClassNotFoundException | NoSuchMethodException e) { - LogUtils.warn("Failed to initialize mcMMO Treasure"); - } - registerLevelPlugin("mcMMO", new McMMOImpl()); - hookMessage("mcMMO"); - } - if (plugin.isHookedPluginEnabled("AureliumSkills")) { - registerLevelPlugin("AureliumSkills", new AureliumSkillsImpl()); - hookMessage("AureliumSkills"); - } - if (plugin.isHookedPluginEnabled("AuraSkills")) { - registerLevelPlugin("AuraSkills", new AuraSkillsImpl()); - hookMessage("AuraSkills"); - } - if (plugin.isHookedPluginEnabled("EcoEnchants")) { - this.enchantmentPluginMap.put("EcoEnchants", new VanillaEnchantmentsImpl()); - hookMessage("EcoEnchants"); - } else { - this.enchantmentPluginMap.put("vanilla", new VanillaEnchantmentsImpl()); - } - if (plugin.isHookedPluginEnabled("AdvancedEnchantments")) { - this.enchantmentPluginMap.put("AdvancedEnchantments", new AdvancedEnchantmentsImpl()); - hookMessage("AdvancedEnchantments"); - } - if (plugin.isHookedPluginEnabled("RealisticSeasons")) { - this.seasonInterface = new RealisticSeasonsImpl(); - } else if (plugin.isHookedPluginEnabled("CustomCrops")) { - this.seasonInterface = new CustomCropsSeasonImpl(); - } - if (plugin.isHookedPluginEnabled("Vault")) { - VaultHook.initialize(); - } - if (plugin.isHookedPluginEnabled("BattlePass")){ - BattlePassHook battlePassHook = new BattlePassHook(); - battlePassHook.register(); - hookMessage("BattlePass"); - } - if (plugin.isHookedPluginEnabled("ClueScrolls")) { - ClueScrollsHook clueScrollsHook = new ClueScrollsHook(); - clueScrollsHook.register(); - hookMessage("ClueScrolls"); - } - if (plugin.isHookedPluginEnabled("BetonQuest")) { - BetonQuestHook.register(); - hookMessage("BetonQuest"); - } -// if (plugin.isHookedPluginEnabled("NotQuests")) { -// NotQuestHook notQuestHook = new NotQuestHook(); -// notQuestHook.register(); -// hookMessage("NotQuests"); -// } - } - - /** - * 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; - levelPluginMap.put(plugin, level); - 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 (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 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 - @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 : 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 deleted file mode 100644 index a754e006..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/item/CustomFishingItemImpl.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.compatibility.item; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.mechanic.item.ItemLibrary; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -public class CustomFishingItemImpl implements ItemLibrary { - - @Override - public String identification() { - return "CustomFishing"; - } - - @Override - public ItemStack buildItem(Player player, String id) { - String[] split = id.split(":", 2); - return CustomFishingPlugin.get().getItemManager().build(player, split[0], split[1]); - } - - @Override - public String getItemID(ItemStack 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 deleted file mode 100644 index 79f5ef63..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/PlaceholderManagerImpl.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.compatibility.papi; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.manager.PlaceholderManager; -import net.momirealms.customfishing.util.ConfigUtils; -import net.objecthunter.exp4j.ExpressionBuilder; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -public class PlaceholderManagerImpl implements PlaceholderManager { - - private static PlaceholderManagerImpl instance; - private final CustomFishingPlugin plugin; - private final boolean hasPapi; - private final Pattern pattern; - private final HashMap customPlaceholderMap; - private CompetitionPapi competitionPapi; - private StatisticsPapi statisticsPapi; - private CFPapi cfPapi; - - public PlaceholderManagerImpl(CustomFishingPlugin plugin) { - instance = this; - this.plugin = plugin; - this.hasPapi = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"); - this.pattern = Pattern.compile("\\{[^{}]+}"); - this.customPlaceholderMap = new HashMap<>(); - if (this.hasPapi) { - competitionPapi = new CompetitionPapi(plugin); - statisticsPapi = new StatisticsPapi(plugin); - cfPapi = new CFPapi(plugin); - } - } - - public void load() { - if (competitionPapi != null) competitionPapi.load(); - if (statisticsPapi != null) statisticsPapi.load(); - if (cfPapi != null) cfPapi.load(); - loadCustomPlaceholders(); - } - - public void unload() { - if (competitionPapi != null) competitionPapi.unload(); - if (statisticsPapi != null) statisticsPapi.unload(); - if (cfPapi != null) cfPapi.unload(); - } - - public void disable() { - this.customPlaceholderMap.clear(); - } - - public void loadCustomPlaceholders() { - YamlConfiguration config = plugin.getConfig("config.yml"); - ConfigurationSection section = config.getConfigurationSection("other-settings.placeholder-register"); - if (section != null) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - registerCustomPlaceholder(entry.getKey(), (String) entry.getValue()); - } - } - } - - @Override - public boolean registerCustomPlaceholder(String placeholder, String original) { - if (this.customPlaceholderMap.containsKey(placeholder)) { - return false; - } - this.customPlaceholderMap.put(placeholder, original); - return true; - } - - /** - * 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<>(); - Matcher matcher = pattern.matcher(text); - while (matcher.find()) placeholders.add(matcher.group()); - 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; - if (placeholders != null) - result = placeholders.get(placeholder); - if (result != null) - return result; - String custom = customPlaceholderMap.get(placeholder); - if (custom == null) - return placeholder; - 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); - for (String papi : list) { - String replacer = null; - if (placeholders != null) { - replacer = placeholders.get(papi); - } - if (replacer == null) { - String custom = customPlaceholderMap.get(papi); - if (custom != null) { - replacer = setPlaceholders(player, parse(player, custom, placeholders)); - } - } - if (replacer != null) { - text = text.replace(papi, replacer); - } - } - 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 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; - } - - public boolean hasPapi() { - return hasPapi; - } - - @Override - public double getExpressionValue(Player player, String formula, Map vars) { - return ConfigUtils.getExpressionValue(player, formula, vars); - } - - @Override - public double getExpressionValue(String formula) { - return new ExpressionBuilder(formula).build().evaluate(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/StatisticsPapi.java b/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/StatisticsPapi.java deleted file mode 100644 index f313ad0e..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/papi/StatisticsPapi.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.compatibility.papi; - -import me.clip.placeholderapi.expansion.PlaceholderExpansion; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.user.OnlineUser; -import net.momirealms.customfishing.api.mechanic.statistic.Statistics; -import org.bukkit.OfflinePlayer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -public class StatisticsPapi extends PlaceholderExpansion { - - private final CustomFishingPlugin plugin; - - public StatisticsPapi(CustomFishingPlugin plugin) { - this.plugin = plugin; - } - - public void load() { - super.register(); - } - - public void unload() { - super.unregister(); - } - - @Override - public @NotNull String getIdentifier() { - return "fishingstats"; - } - - @Override - public @NotNull String getAuthor() { - return "XiaoMoMi"; - } - - @Override - public @NotNull String getVersion() { - return "2.0"; - } - - @Override - public boolean persist() { - return true; - } - - @Override - public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) { - OnlineUser onlineUser = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); - if (onlineUser == null) return "Data not loaded"; - Statistics statistics = onlineUser.getStatistics(); - String[] split = params.split("_", 2); - switch (split[0]) { - case "total" -> { - return String.valueOf(statistics.getTotalCatchAmount()); - } - case "hascaught" -> { - if (split.length == 1) return "Invalid format"; - return String.valueOf(statistics.getLootAmount(split[1]) != 0); - } - case "amount" -> { - if (split.length == 1) return "Invalid format"; - return String.valueOf(statistics.getLootAmount(split[1])); - } - case "size-record" -> { - return String.format("%.2f", statistics.getSizeRecord(split[1])); - } - case "category" -> { - if (split.length == 1) return "Invalid format"; - String[] categorySplit = split[1].split("_", 2); - if (categorySplit.length == 1) return "Invalid format"; - List category = plugin.getStatisticsManager().getCategory(categorySplit[1]); - if (category == null) return "Category Not Exists"; - if (categorySplit[0].equals("total")) { - int total = 0; - for (String loot : category) { - total += statistics.getLootAmount(loot); - } - return String.valueOf(total); - } else if (categorySplit[0].equals("progress")) { - int size = category.size(); - int unlocked = 0; - for (String loot : category) { - if (statistics.getLootAmount(loot) != 0) unlocked++; - } - double percent = ((double) unlocked * 100) / size; - String progress = String.format("%.1f", percent); - return progress.equals("100.0") ? "100" : progress; - } - } - } - - return "null"; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/compatibility/quest/NotQuestHook.java b/plugin/src/main/java/net/momirealms/customfishing/compatibility/quest/NotQuestHook.java deleted file mode 100644 index aec0f0c1..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/compatibility/quest/NotQuestHook.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.compatibility.quest; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.event.FishingResultEvent; -import net.momirealms.customfishing.api.mechanic.loot.Loot; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.checkerframework.checker.nullness.qual.Nullable; -import rocks.gravili.notquests.paper.NotQuests; -import rocks.gravili.notquests.paper.structs.ActiveObjective; -import rocks.gravili.notquests.paper.structs.ActiveQuest; -import rocks.gravili.notquests.paper.structs.QuestPlayer; -import rocks.gravili.notquests.paper.structs.objectives.Objective; - -import java.util.Map; - -public class NotQuestHook implements Listener { - - private final NotQuests notQuestsInstance; - - public NotQuestHook() { - this.notQuestsInstance = NotQuests.getInstance(); - } - - @EventHandler - public void onFish(FishingResultEvent event) { - if (event.isCancelled() || event.getResult() == FishingResultEvent.Result.FAILURE) - return; - Loot loot = event.getLoot(); - Player player = event.getPlayer(); - final QuestPlayer questPlayer = notQuestsInstance.getQuestPlayerManager().getActiveQuestPlayer(player.getUniqueId()); - if (questPlayer != null) { - if (questPlayer.getActiveQuests().size() > 0) { - for (final ActiveQuest activeQuest : questPlayer.getActiveQuests()) { - for (final ActiveObjective activeObjective : activeQuest.getActiveObjectives()) { - if (activeObjective.getObjective() instanceof GroupObjective groupObjective) { - if (activeObjective.isUnlocked()) { - final String[] groups = loot.getLootGroup(); - if (groups != null) - for (String group : groups) { - if (group.equals(groupObjective.getGroupToFish())) { - activeObjective.addProgress(event.getAmount()); - } - } - } - } else if (activeObjective.getObjective() instanceof LootObjective lootObjective) { - if (activeObjective.isUnlocked()) { - if (lootObjective.getLootID().equals(loot.getID()) || lootObjective.getLootID().equals("any")) { - activeObjective.addProgress(event.getAmount()); - } - } - } - } - activeQuest.removeCompletedObjectives(true); - } - questPlayer.removeCompletedQuests(); - } - } - } - - public void register() { - Bukkit.getPluginManager().registerEvents(this, CustomFishingPlugin.get()); - notQuestsInstance.getObjectiveManager().registerObjective("CustomFishingGroup", GroupObjective.class); - notQuestsInstance.getObjectiveManager().registerObjective("CustomFishingGroup", GroupObjective.class); - } - - public static class GroupObjective extends Objective { - - private String group; - - public GroupObjective(NotQuests main) { - super(main); - } - - @Override - protected String getTaskDescriptionInternal(QuestPlayer questPlayer, @Nullable ActiveObjective activeObjective) { - return main.getLanguageManager() - .getString( - "chat.objectives.taskDescription.customfishingGroup.base", - questPlayer, - activeObjective, - Map.of("%CUSTOMFISHINGGROUP%", getGroupToFish())); - } - - @Override - public void save(FileConfiguration fileConfiguration, String initialPath) { - fileConfiguration.set(initialPath + ".specifics.group", getGroupToFish()); - } - - @Override - public void load(FileConfiguration fileConfiguration, String initialPath) { - group = fileConfiguration.getString(initialPath + ".specifics.group"); - } - - @Override - public void onObjectiveUnlock(ActiveObjective activeObjective, boolean b) { - } - - @Override - public void onObjectiveCompleteOrLock(ActiveObjective activeObjective, boolean b, boolean b1) { - } - - public String getGroupToFish() { - return group; - } - } - - public static class LootObjective extends Objective { - - private String loot; - - public LootObjective(NotQuests main) { - super(main); - } - - @Override - protected String getTaskDescriptionInternal(QuestPlayer questPlayer, @Nullable ActiveObjective activeObjective) { - String toReturn; - if (!getLootID().isBlank() && !getLootID().equals("any")) { - toReturn = - main.getLanguageManager() - .getString( - "chat.objectives.taskDescription.customfishingLoot.base", - questPlayer, - activeObjective, - Map.of("%CUSTOMFISHINGLOOT%", getLootID())); - } else { - toReturn = - main.getLanguageManager() - .getString( - "chat.objectives.taskDescription.customfishingLoot.any", - questPlayer, - activeObjective); - } - return toReturn; - } - - @Override - public void save(FileConfiguration fileConfiguration, String initialPath) { - fileConfiguration.set(initialPath + ".specifics.id", getLootID()); - } - - @Override - public void load(FileConfiguration fileConfiguration, String initialPath) { - loot = fileConfiguration.getString(initialPath + ".specifics.id"); - } - - @Override - public void onObjectiveUnlock(ActiveObjective activeObjective, boolean b) { - } - - @Override - public void onObjectiveCompleteOrLock(ActiveObjective activeObjective, boolean b, boolean b1) { - } - - public String getLootID() { - return loot; - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/SectionPage.java b/plugin/src/main/java/net/momirealms/customfishing/gui/SectionPage.java deleted file mode 100644 index 9ea86ecf..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/SectionPage.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui; - -import org.bukkit.configuration.ConfigurationSection; - -public interface SectionPage extends YamlPage { - - ConfigurationSection getSection(); -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackToFolderItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackToFolderItem.java deleted file mode 100644 index 5b348c4b..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackToFolderItem.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.Icon; -import net.momirealms.customfishing.gui.page.file.FileSelector; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -import java.io.File; -import java.util.List; - -public class BackToFolderItem extends AbstractItem implements Icon { - - private final File file; - - public BackToFolderItem(File file) { - this.file = file; - } - - @Override - public ItemProvider getItemProvider() { - if (file != null && (file.getPath().startsWith("plugins\\CustomFishing\\contents") || file.getPath().startsWith("plugins/CustomFishing/contents"))) { - return new ItemBuilder(Material.ORANGE_STAINED_GLASS_PANE) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_BACK_TO_PARENT_FOLDER - ))) - .setLore(List.of(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "<#FFA500>-> " + file.getName() - )))); - } else { - return new ItemBuilder(Material.BLACK_STAINED_GLASS_PANE); - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (file != null && (file.getPath().startsWith("plugins\\CustomFishing\\contents") || file.getPath().startsWith("plugins/CustomFishing/contents"))) - new FileSelector(player, file); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackToPageItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackToPageItem.java deleted file mode 100644 index f5fb2cca..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/BackToPageItem.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.ParentPage; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class BackToPageItem extends AbstractItem { - - private final ParentPage parentPage; - - public BackToPageItem(ParentPage parentPage) { - this.parentPage = parentPage; - } - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.ORANGE_STAINED_GLASS_PANE) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_BACK_TO_PARENT_PAGE - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - parentPage.reOpen(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/NextPageItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/NextPageItem.java deleted file mode 100644 index 6da7372d..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/NextPageItem.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.Icon; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.controlitem.PageItem; - -public class NextPageItem extends PageItem implements Icon { - - public NextPageItem() { - super(true); - } - - @Override - public ItemProvider getItemProvider(PagedGui gui) { - ItemBuilder builder = new ItemBuilder(Material.GREEN_STAINED_GLASS_PANE); - builder.setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NEXT_PAGE - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - gui.hasNextPage() - ? CFLocale.GUI_GOTO_NEXT_PAGE - .replace("{0}", String.valueOf(gui.getCurrentPage() + 2)) - .replace("{1}", String.valueOf(gui.getPageAmount())) - : CFLocale.GUI_CANNOT_GOTO_NEXT_PAGE - ))); - return builder; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/PreviousPageItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/PreviousPageItem.java deleted file mode 100644 index 8ea9fc89..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/PreviousPageItem.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.Icon; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.controlitem.PageItem; - -public class PreviousPageItem extends PageItem implements Icon { - - public PreviousPageItem() { - super(false); - } - - @Override - public ItemProvider getItemProvider(PagedGui gui) { - ItemBuilder builder = new ItemBuilder(Material.RED_STAINED_GLASS_PANE); - builder.setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_PREVIOUS_PAGE - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - gui.hasPreviousPage() - ? CFLocale.GUI_GOTO_PREVIOUS_PAGE - .replace("{0}", String.valueOf(gui.getCurrentPage())) - .replace("{1}", String.valueOf(gui.getPageAmount())) - : CFLocale.GUI_CANNOT_GOTO_PREVIOUS_PAGE - ))); - return builder; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollDownItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollDownItem.java deleted file mode 100644 index 0426248c..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollDownItem.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.Icon; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import xyz.xenondevs.invui.gui.ScrollGui; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem; - -public class ScrollDownItem extends ScrollItem implements Icon { - - public ScrollDownItem() { - super(1); - } - - @Override - public ItemProvider getItemProvider(ScrollGui gui) { - ItemBuilder builder = new ItemBuilder(Material.GREEN_STAINED_GLASS_PANE); - builder.setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_SCROLL_DOWN - ))); - if (!gui.canScroll(1)) - builder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CANNOT_SCROLL_DOWN - ))); - return builder; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollUpItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollUpItem.java deleted file mode 100644 index c040c611..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/ScrollUpItem.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.Icon; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import xyz.xenondevs.invui.gui.ScrollGui; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem; - -public class ScrollUpItem extends ScrollItem implements Icon { - - public ScrollUpItem() { - super(-1); - } - - @Override - public ItemProvider getItemProvider(ScrollGui gui) { - ItemBuilder builder = new ItemBuilder(Material.RED_STAINED_GLASS_PANE); - builder.setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_SCROLL_UP - ))); - if (!gui.canScroll(-1)) - builder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CANNOT_SCROLL_UP - ))); - return builder; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/AmountItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/AmountItem.java deleted file mode 100644 index f47c40da..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/AmountItem.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.AmountEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class AmountItem extends AbstractItem { - - private final SectionPage itemPage; - - public AmountItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.IRON_NUGGET) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_AMOUNT - ))) - .setAmount(itemPage.getSection().getInt("amount", 1)); - if (itemPage.getSection().contains("amount")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getInt("amount") - ))) - .addLoreLines(""); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new AmountEditor(player, itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("amount", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/CMDItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/CMDItem.java deleted file mode 100644 index ae3cec8b..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/CMDItem.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.CustomModelDataEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class CMDItem extends AbstractItem { - - private final SectionPage itemPage; - - public CMDItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.GLOW_INK_SAC) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_MODEL_DATA - ))); - if (itemPage.getSection().contains("custom-model-data")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getInt("custom-model-data") - ))) - .addLoreLines(""); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new CustomModelDataEditor(player, itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("custom-model-data", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/DisplayNameItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/DisplayNameItem.java deleted file mode 100644 index 3fe0e95b..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/DisplayNameItem.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.DisplayNameEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class DisplayNameItem extends AbstractItem { - - private final SectionPage itemPage; - - public DisplayNameItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.NAME_TAG) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_DISPLAY_NAME - ))); - if (itemPage.getSection().contains("display.name")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getString("display.name") - ))) - .addLoreLines(""); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new DisplayNameEditor(player, itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("display.name", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/DurabilityItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/DurabilityItem.java deleted file mode 100644 index 7341393d..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/DurabilityItem.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.DurabilityEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class DurabilityItem extends AbstractItem { - - private final SectionPage itemPage; - - public DurabilityItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.DIAMOND_CHESTPLATE) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_DURABILITY - ))); - if (itemPage.getSection().contains("max-durability")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getInt("max-durability") - ))) - .addLoreLines(""); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new DurabilityEditor(player, itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("max-durability", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/EnchantmentItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/EnchantmentItem.java deleted file mode 100644 index fe73a540..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/EnchantmentItem.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.EnchantmentEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemFlag; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -import java.util.Map; - -public class EnchantmentItem extends AbstractItem { - - private final SectionPage itemPage; - - public EnchantmentItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.IRON_HOE) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_ENCHANTMENT - ))) - .addEnchantment(Enchantment.ARROW_FIRE,1,true) - .addItemFlags(ItemFlag.HIDE_ENCHANTS); - if (itemPage.getSection().contains("enchantments")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE - ))); - for (Map.Entry entry : itemPage.getSection().getConfigurationSection("enchantments").getValues(false).entrySet()) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - " - " + entry.getKey() + ":" + entry.getValue() - ))); - } - itemBuilder.addLoreLines("").addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new EnchantmentEditor(player, itemPage, false); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("enchantments", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/Head64Item.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/Head64Item.java deleted file mode 100644 index ca213160..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/Head64Item.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -import java.util.ArrayList; - -public class Head64Item extends AbstractItem { - - private final SectionPage itemPage; - - public Head64Item(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.PLAYER_HEAD) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_HEAD64 - ))); - if (itemPage.getSection().contains("head64")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE - ))); - String head64 = itemPage.getSection().getString("head64", ""); - ArrayList list = new ArrayList<>(); - for (int i = 0; i < head64.length(); i += 16) { - if (i + 16 > head64.length()) { - list.add(head64.substring(i)); - } else { - list.add(head64.substring(i, i + 16)); - } - } - for (String line : list) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - ""+ line - ))); - } - itemBuilder.addLoreLines("").addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - player.closeInventory(); - AdventureHelper.getInstance().sendMessageWithPrefix(player, "Input the head64 value in chat"); - ((CustomFishingPluginImpl) CustomFishingPlugin.get()).getChatCatcherManager().catchMessage(player, "head64", itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("head64", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/ItemFlagItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/ItemFlagItem.java deleted file mode 100644 index 90f70925..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/ItemFlagItem.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.ItemFlagEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class ItemFlagItem extends AbstractItem { - - private final SectionPage itemPage; - - public ItemFlagItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.CYAN_BANNER) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_FLAG - ))); - if (itemPage.getSection().contains("item-flags")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE - ))); - for (String lore : itemPage.getSection().getStringList("item-flags")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - " - " + lore - ))); - } - itemBuilder.addLoreLines(""); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new ItemFlagEditor(player, itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("item-flags", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/LoreItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/LoreItem.java deleted file mode 100644 index f5525a7d..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/LoreItem.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.LoreEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class LoreItem extends AbstractItem { - - private final SectionPage itemPage; - - public LoreItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.BIRCH_SIGN) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_LORE - ))); - if (itemPage.getSection().contains("display.lore")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE - ))); - for (String lore : itemPage.getSection().getStringList("display.lore")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - " - " + lore - ))); - } - itemBuilder.addLoreLines(""); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new LoreEditor(player, itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("display.lore", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/MaterialItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/MaterialItem.java deleted file mode 100644 index ac638535..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/MaterialItem.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.MaterialEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class MaterialItem extends AbstractItem { - - private final SectionPage itemPage; - - public MaterialItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.COD) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_MATERIAL - ))); - if (itemPage.getSection().contains("material")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getString("material") - ))) - .addLoreLines(""); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new MaterialEditor(player, itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("material", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/NBTItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/NBTItem.java deleted file mode 100644 index df3b1ece..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/NBTItem.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.NBTEditor; -import net.momirealms.customfishing.setting.CFLocale; -import net.momirealms.customfishing.util.ConfigUtils; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class NBTItem extends AbstractItem { - - private final SectionPage itemPage; - - public NBTItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.COMMAND_BLOCK) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_NBT - ))); - var section = itemPage.getSection().getConfigurationSection("nbt"); - if (section != null) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE - ))); - for (String line : ConfigUtils.getReadableSection(section.getValues(false))) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - line - ))); - } - itemBuilder.addLoreLines("").addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new NBTEditor(player, itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("nbt", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/PreventGrabItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/PreventGrabItem.java deleted file mode 100644 index 436e2031..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/PreventGrabItem.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class PreventGrabItem extends AbstractItem { - - private final SectionPage itemPage; - - public PreventGrabItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.DRAGON_EGG) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_PREVENT_GRAB - ))); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getBoolean("prevent-grabbing", false) - ))) - .addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_TO_TOGGLE - ))); - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - itemPage.getSection().set("prevent-grabbing", !itemPage.getSection().getBoolean("prevent-grabbing", false)); - itemPage.save(); - itemPage.reOpen(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/PriceItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/PriceItem.java deleted file mode 100644 index bfcd5080..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/PriceItem.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.PriceEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class PriceItem extends AbstractItem { - - private final SectionPage itemPage; - - public PriceItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.GOLD_INGOT) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_PRICE - ))); - if (itemPage.getSection().contains("price")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_PRICE_BASE + itemPage.getSection().getDouble("price.base") - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_PRICE_BONUS + itemPage.getSection().getDouble("price.bonus") - ))) - .addLoreLines(""); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new PriceEditor(player, itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("price", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/RandomDurabilityItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/RandomDurabilityItem.java deleted file mode 100644 index e94841c6..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/RandomDurabilityItem.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class RandomDurabilityItem extends AbstractItem { - - private final SectionPage itemPage; - - public RandomDurabilityItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.LEATHER_BOOTS) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_RANDOM_DURABILITY - ))) - .setDamage(15); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getBoolean("random-durability", false) - ))) - .addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_TO_TOGGLE - ))); - - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - itemPage.getSection().set("random-durability", !itemPage.getSection().getBoolean("random-durability", false)); - itemPage.save(); - itemPage.reOpen(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/SizeItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/SizeItem.java deleted file mode 100644 index 36a461a2..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/SizeItem.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.SizeEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class SizeItem extends AbstractItem { - - private final SectionPage itemPage; - - public SizeItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.PUFFERFISH) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_SIZE - ))); - if (itemPage.getSection().contains("size")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getString("size") - ))) - .addLoreLines(""); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new SizeEditor(player, itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("size", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/StackableItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/StackableItem.java deleted file mode 100644 index b5f3c504..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/StackableItem.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class StackableItem extends AbstractItem { - - private final SectionPage itemPage; - - public StackableItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.CHEST_MINECART) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_STACKABLE - ))); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getBoolean("stackable", true) - ))) - .addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_TO_TOGGLE - ))); - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - itemPage.getSection().set("stackable", !itemPage.getSection().getBoolean("stackable", true)); - itemPage.save(); - itemPage.reOpen(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/StoredEnchantmentItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/StoredEnchantmentItem.java deleted file mode 100644 index c55c5900..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/StoredEnchantmentItem.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.EnchantmentEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemFlag; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -import java.util.Map; - -public class StoredEnchantmentItem extends AbstractItem { - - private final SectionPage itemPage; - - public StoredEnchantmentItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.ENCHANTED_BOOK) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_STORED_ENCHANTMENT - ))) - .addEnchantment(Enchantment.ARROW_FIRE,1,true) - .addItemFlags(ItemFlag.HIDE_ENCHANTS); - if (itemPage.getSection().contains("stored-enchantments")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE - ))); - for (Map.Entry entry : itemPage.getSection().getConfigurationSection("stored-enchantments").getValues(false).entrySet()) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - " - " + entry.getKey() + ":" + entry.getValue() - ))); - } - itemBuilder.addLoreLines("").addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new EnchantmentEditor(player, itemPage, true); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("stored-enchantments", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/TagItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/TagItem.java deleted file mode 100644 index 694870d0..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/TagItem.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class TagItem extends AbstractItem { - - private final SectionPage itemPage; - - public TagItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.TOTEM_OF_UNDYING) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_TAG - ))); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getBoolean("tag", true) - ))) - .addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_TO_TOGGLE - ))); - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - itemPage.getSection().set("tag", !itemPage.getSection().getBoolean("tag", true)); - itemPage.save(); - itemPage.reOpen(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/UnbreakableItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/UnbreakableItem.java deleted file mode 100644 index 30e3ee31..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/item/UnbreakableItem.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class UnbreakableItem extends AbstractItem { - - private final SectionPage itemPage; - - public UnbreakableItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.BEDROCK) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_UNBREAKABLE - ))); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getBoolean("unbreakable", false) - ))) - .addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_TO_TOGGLE - ))); - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - itemPage.getSection().set("unbreakable", !itemPage.getSection().getBoolean("unbreakable", false)); - itemPage.save(); - itemPage.reOpen(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/DisableGameItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/DisableGameItem.java deleted file mode 100644 index d314f5da..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/DisableGameItem.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.loot; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class DisableGameItem extends AbstractItem { - - private final SectionPage itemPage; - - public DisableGameItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.LEAD) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LOOT_DISABLE_GAME - ))); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getBoolean("disable-game", false) - ))) - .addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_TO_TOGGLE - ))); - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - itemPage.getSection().set("disable-game", !itemPage.getSection().getBoolean("disable-game", false)); - itemPage.save(); - itemPage.reOpen(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/DisableStatsItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/DisableStatsItem.java deleted file mode 100644 index 5527128b..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/DisableStatsItem.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.loot; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class DisableStatsItem extends AbstractItem { - - private final SectionPage itemPage; - - public DisableStatsItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.WRITTEN_BOOK) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LOOT_DISABLE_STATS - ))); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getBoolean("disable-stat", false) - ))) - .addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_TO_TOGGLE - ))); - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - itemPage.getSection().set("disable-game", !itemPage.getSection().getBoolean("disable-stat", false)); - itemPage.save(); - itemPage.reOpen(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/InstantGameItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/InstantGameItem.java deleted file mode 100644 index 5109dd1e..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/InstantGameItem.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.loot; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class InstantGameItem extends AbstractItem { - - private final SectionPage itemPage; - - public InstantGameItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.FISHING_ROD) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LOOT_INSTANT_GAME - ))); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getBoolean("instant-game", false) - ))) - .addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_TO_TOGGLE - ))); - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - itemPage.getSection().set("instant-game", !itemPage.getSection().getBoolean("instant-game", false)); - itemPage.save(); - itemPage.reOpen(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/NickItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/NickItem.java deleted file mode 100644 index f208f219..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/NickItem.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.loot; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.NickEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class NickItem extends AbstractItem { - - private final SectionPage itemPage; - - public NickItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.WRITABLE_BOOK) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LOOT_NICK - ))); - if (itemPage.getSection().contains("nick")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getString("nick") - ))) - .addLoreLines(""); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new NickEditor(player, itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("nick", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/ScoreItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/ScoreItem.java deleted file mode 100644 index c953d39c..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/ScoreItem.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.loot; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.page.property.ScoreEditor; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class ScoreItem extends AbstractItem { - - private final SectionPage itemPage; - - public ScoreItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.NETHER_STAR) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LOOT_SCORE - ))); - if (itemPage.getSection().contains("score")) { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getDouble("score") - ))) - .addLoreLines(""); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_RESET - ))); - } else { - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))); - } - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - new ScoreEditor(player, itemPage); - } else if (clickType.isRightClick()) { - itemPage.getSection().set("score", null); - itemPage.save(); - itemPage.reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/ShowInFinderItem.java b/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/ShowInFinderItem.java deleted file mode 100644 index 9154e529..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/icon/property/loot/ShowInFinderItem.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.icon.property.loot; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; - -public class ShowInFinderItem extends AbstractItem { - - private final SectionPage itemPage; - - public ShowInFinderItem(SectionPage itemPage) { - this.itemPage = itemPage; - } - - @Override - public ItemProvider getItemProvider() { - ItemBuilder itemBuilder = new ItemBuilder(Material.COMPASS) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LOOT_SHOW_IN_FINDER - ))); - itemBuilder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CURRENT_VALUE + itemPage.getSection().getBoolean("show-in-fishfinder", true) - ))) - .addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_TO_TOGGLE - ))); - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - itemPage.getSection().set("show-in-fishfinder", !itemPage.getSection().getBoolean("show-in-fishfinder", true)); - itemPage.save(); - itemPage.reOpen(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/file/FileSelector.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/file/FileSelector.java deleted file mode 100644 index 31953891..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/file/FileSelector.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.file; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.gui.icon.BackToFolderItem; -import net.momirealms.customfishing.gui.icon.ScrollDownItem; -import net.momirealms.customfishing.gui.icon.ScrollUpItem; -import net.momirealms.customfishing.gui.page.item.ItemSelector; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.ScrollGui; -import xyz.xenondevs.invui.gui.structure.Markers; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.window.Window; - -import java.io.File; -import java.util.ArrayDeque; -import java.util.Deque; - -public class FileSelector { - - public FileSelector(Player player, File folder) { - File[] files = folder.listFiles(); - Deque items = new ArrayDeque<>(); - if (files != null) { - for (File file : files) { - if (file.isFile() && file.getName().endsWith(".yml")) { - items.addLast(new FileItem(file)); - } else if (file.isDirectory()) { - String path = file.getPath().replace("/", "\\"); - String[] split = path.split("\\\\"); - String type = split[3]; - switch (type) { - case "item", "rod", "bait", "util", "hook" -> items.addFirst(new FolderItem(file)); - } - } - } - } - - Gui gui = ScrollGui.items() - .setStructure( - "x x x x x x x x u", - "x x x x x x x x #", - "x x x x x x x x b", - "x x x x x x x x #", - "x x x x x x x x d" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('#', new BackGroundItem()) - .addIngredient('u', new ScrollUpItem()) - .addIngredient('d', new ScrollDownItem()) - .addIngredient('b', new BackToFolderItem(folder.getParentFile())) - .setContent(items.stream().toList()) - .build(); - - Window window = Window.single() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_SELECT_FILE - ))) - .setGui(gui) - .build(); - -// gui.playAnimation(new SequentialAnimation(1, true), slotElement -> { -// if (slotElement instanceof SlotElement.ItemSlotElement itemSlotElement) { -// return !(itemSlotElement.getItem() instanceof Icon); -// } -// return true; -// }); - - window.open(); - } - - public static class FileItem extends AbstractItem { - - private final File file; - - public FileItem(File file) { - this.file = file; - } - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.PAPER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "<#FDF5E6>" + file.getName() - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - String path = file.getPath().replace("/", "\\"); - String[] split = path.split("\\\\"); - String type = split[3]; - switch (type) { - case "item", "rod", "bait", "util", "hook" -> { - new ItemSelector(player, file, type); - } - } - } - } - - public static class FolderItem extends AbstractItem { - - private final File file; - - public FolderItem(File file) { - this.file = file; - } - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.BOOK).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "<#D2B48C>" + file.getName() - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - new FileSelector(player, file); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/AbstractSectionEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/AbstractSectionEditor.java deleted file mode 100644 index 3121eff8..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/AbstractSectionEditor.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.item; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.gui.icon.BackToPageItem; -import net.momirealms.customfishing.gui.icon.NextPageItem; -import net.momirealms.customfishing.gui.icon.PreviousPageItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.gui.structure.Markers; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -import java.util.List; - -public abstract class AbstractSectionEditor implements SectionPage { - - protected final Player player; - protected final ItemSelector itemSelector; - protected final ConfigurationSection section; - protected final String key; - - public AbstractSectionEditor(Player player, ItemSelector itemSelector, ConfigurationSection section, String key) { - this.player = player; - this.itemSelector = itemSelector; - this.section = section; - this.key = key; - this.reOpen(); - } - - @Override - public ConfigurationSection getSection() { - return section; - } - - @Override - public void reOpen() { - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - Gui upperGui = Gui.normal() - .setStructure( - "# a #" - ) - .addIngredient('a', new RefreshExample()) - .addIngredient('#', border) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # a # c # b # #" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('#', new BackGroundItem()) - .addIngredient('a', new PreviousPageItem()) - .addIngredient('b', new NextPageItem()) - .addIngredient('c', new BackToPageItem(itemSelector)) - .setContent(getItemList()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_EDIT_KEY.replace("{0}", key)) - )) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - @Override - public void save() { - itemSelector.save(); - } - - public class RefreshExample extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(CustomFishingPlugin.get().getItemManager().getItemBuilder(section, "bait", key).build(player)); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - notifyWindows(); - } - } - - public abstract List getItemList(); -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/BaitEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/BaitEditor.java deleted file mode 100644 index 57295a35..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/BaitEditor.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.item; - -import net.momirealms.customfishing.gui.icon.property.item.*; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import xyz.xenondevs.invui.item.Item; - -import java.util.ArrayList; -import java.util.List; - -@SuppressWarnings("DuplicatedCode") -public class BaitEditor extends AbstractSectionEditor { - - public BaitEditor(Player player, String key, ItemSelector itemSelector, ConfigurationSection section) { - super(player, itemSelector, section, key); - } - - @Override - public List getItemList() { - ArrayList items = new ArrayList<>(); - items.add(new MaterialItem(this)); - items.add(new DisplayNameItem(this)); - items.add(new LoreItem(this)); - items.add(new CMDItem(this)); - items.add(new TagItem(this)); - items.add(new UnbreakableItem(this)); - items.add(new DurabilityItem(this)); - items.add(new RandomDurabilityItem(this)); - items.add(new StackableItem(this)); - items.add(new ItemFlagItem(this)); - items.add(new Head64Item(this)); - items.add(new NBTItem(this)); - items.add(new EnchantmentItem(this)); - items.add(new StoredEnchantmentItem(this)); - return items; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/HookEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/HookEditor.java deleted file mode 100644 index 1ac8e4a6..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/HookEditor.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.item; - -import net.momirealms.customfishing.gui.icon.property.item.*; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import xyz.xenondevs.invui.item.Item; - -import java.util.ArrayList; -import java.util.List; - -@SuppressWarnings("DuplicatedCode") -public class HookEditor extends AbstractSectionEditor { - - public HookEditor(Player player, String key, ItemSelector itemSelector, ConfigurationSection section) { - super(player, itemSelector, section, key); - } - - @Override - public List getItemList() { - ArrayList items = new ArrayList<>(); - items.add(new MaterialItem(this)); - items.add(new DisplayNameItem(this)); - items.add(new LoreItem(this)); - items.add(new CMDItem(this)); - items.add(new TagItem(this)); - items.add(new UnbreakableItem(this)); - items.add(new DurabilityItem(this)); - items.add(new RandomDurabilityItem(this)); - items.add(new StackableItem(this)); - items.add(new ItemFlagItem(this)); - items.add(new Head64Item(this)); - items.add(new NBTItem(this)); - items.add(new EnchantmentItem(this)); - items.add(new StoredEnchantmentItem(this)); - return items; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/ItemSelector.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/ItemSelector.java deleted file mode 100644 index 4500c195..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/ItemSelector.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.item; - -import de.tr7zw.changeme.nbtapi.NBTItem; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.gui.YamlPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.gui.icon.BackToFolderItem; -import net.momirealms.customfishing.gui.icon.NextPageItem; -import net.momirealms.customfishing.gui.icon.PreviousPageItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.gui.structure.Markers; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class ItemSelector implements YamlPage { - - private final String SEARCH; - private final Player player; - private final YamlConfiguration yaml; - private String prefix; - private final File file; - private long coolDown; - private final String type; - - public ItemSelector(Player player, File file, String type) { - this.yaml = YamlConfiguration.loadConfiguration(file); - this.player = player; - this.file = file; - this.type = type; - this.SEARCH = CFLocale.GUI_SEARCH; - this.prefix = SEARCH; - this.reOpenWithFilter(SEARCH); - } - - @Override - public void reOpen() { - reOpenWithFilter(prefix); - } - - public void reOpenWithFilter(String filter) { - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - Gui upperGui = Gui.normal() - .setStructure("a # #") - .addIngredient('a', new SimpleItem(new ItemBuilder(Material.NAME_TAG).setDisplayName(filter))) - .addIngredient('#', border) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # a # c # b # #" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('#', new BackGroundItem()) - .addIngredient('a', new PreviousPageItem()) - .addIngredient('b', new NextPageItem()) - .addIngredient('c', new BackToFolderItem(file.getParentFile())) - .setContent(getItemList()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_SELECT_ITEM - ))) - .addRenameHandler(s -> { - long current = System.currentTimeMillis(); - if (current - coolDown < 100) return; - if (s.equals(filter)) return; - prefix = s; - coolDown = current; - reOpenWithFilter(s); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public void reOpenWithNewKey() { - String tempKey = CFLocale.GUI_TEMP_NEW_KEY; - prefix = tempKey; - var confirmIcon = new ConfirmIcon(); - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - Gui upperGui = Gui.normal() - .setStructure("a # b") - .addIngredient('a', new SimpleItem(new ItemBuilder(Material.NAME_TAG).setDisplayName(tempKey))) - .addIngredient('b', confirmIcon) - .addIngredient('#', border) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # a # c # b # #" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('#', new BackGroundItem()) - .addIngredient('a', new PreviousPageItem()) - .addIngredient('b', new NextPageItem()) - .addIngredient('c', new BackToFolderItem(file.getParentFile())) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_SET_NEW_KEY) - )) - .addRenameHandler(s -> { - long current = System.currentTimeMillis(); - if (current - coolDown < 100) return; - if (s.equals(tempKey)) return; - prefix = s; - coolDown = current; - confirmIcon.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public List getItemList() { - List itemList = new ArrayList<>(); - for (Map.Entry entry : this.yaml.getValues(false).entrySet()) { - String key = entry.getKey(); - if (entry.getValue() instanceof ConfigurationSection section) { - if (!prefix.equals(SEARCH) && !entry.getKey().startsWith(prefix)) continue; - String material = section.getString("material"); - if (material != null) { - ItemStack build = CustomFishingPlugin.get().getItemManager().getItemBuilder(section, type, key).build(player); - NBTItem nbtItem = new NBTItem(build); - nbtItem.removeKey("display"); - ItemBuilder itemBuilder = new ItemBuilder(nbtItem.getItem()); - itemList.add(new ItemInList(key, itemBuilder, this)); - continue; - } - } - itemList.add(new ItemInList(key, new ItemBuilder(Material.STRUCTURE_VOID), this)); - } - itemList.add(new AddKey()); - return itemList; - } - - public void removeKey(String key) { - yaml.set(key, null); - } - - public void openEditor(String key) { - switch (type) { - case "item" -> new SectionEditor(player, key, this, yaml.getConfigurationSection(key)); - case "rod" -> new RodEditor(player, key, this, yaml.getConfigurationSection(key)); - case "bait" -> new BaitEditor(player, key, this, yaml.getConfigurationSection(key)); - case "hook" -> new HookEditor(player, key, this, yaml.getConfigurationSection(key)); - } - } - - @Override - public void save() { - try { - yaml.save(file); - } catch (IOException e) { - LogUtils.warn("Failed to save file", e); - } - } - - public static class ItemInList extends AbstractItem { - - private final String key; - private final ItemBuilder itemBuilder; - private final ItemSelector itemSelector; - - public ItemInList(String key, ItemBuilder itemBuilder, ItemSelector itemSelector) { - this.key = key; - this.itemBuilder = itemBuilder.setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - key - ))).addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_DELETE - ))); - this.itemSelector = itemSelector; - } - - @Override - public ItemProvider getItemProvider() { - return itemBuilder; - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - this.itemSelector.openEditor(key); - } else if (clickType.isRightClick()) { - this.itemSelector.removeKey(key); - this.itemSelector.save(); - this.itemSelector.reOpenWithFilter(itemSelector.prefix); - } - } - } - - public class AddKey extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.ANVIL).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ADD_NEW_KEY - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - reOpenWithNewKey(); - } - } - - public class ConfirmIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (prefix != null && !yaml.contains(prefix) && prefix.matches("^[a-zA-Z0-9_]+$")) { - var builder = new ItemBuilder(Material.NAME_TAG) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - prefix - ))); - builder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_CANCEL - ))); - return builder; - } else { - return new ItemBuilder(Material.BARRIER) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DUPE_INVALID_KEY - ))); - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - if (prefix != null && !yaml.contains(prefix) && prefix.matches("^[a-zA-Z0-9_]+$")) { - yaml.createSection(prefix); - save(); - } else { - return; - } - } - prefix = SEARCH; - reOpenWithFilter(SEARCH); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/RodEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/RodEditor.java deleted file mode 100644 index b6d35262..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/RodEditor.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.item; - -import net.momirealms.customfishing.gui.icon.property.item.*; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import xyz.xenondevs.invui.item.Item; - -import java.util.ArrayList; -import java.util.List; - -@SuppressWarnings("DuplicatedCode") -public class RodEditor extends AbstractSectionEditor { - - public RodEditor(Player player, String key, ItemSelector itemSelector, ConfigurationSection section) { - super(player, itemSelector, section, key); - } - - @Override - public List getItemList() { - ArrayList items = new ArrayList<>(); - items.add(new MaterialItem(this)); - items.add(new DisplayNameItem(this)); - items.add(new LoreItem(this)); - items.add(new CMDItem(this)); - items.add(new TagItem(this)); - items.add(new UnbreakableItem(this)); - items.add(new DurabilityItem(this)); - items.add(new RandomDurabilityItem(this)); - items.add(new ItemFlagItem(this)); - items.add(new NBTItem(this)); - items.add(new EnchantmentItem(this)); - return items; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/SectionEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/SectionEditor.java deleted file mode 100644 index 1b314394..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/SectionEditor.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.item; - -import net.momirealms.customfishing.gui.icon.property.item.*; -import net.momirealms.customfishing.gui.icon.property.loot.*; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import xyz.xenondevs.invui.item.Item; - -import java.util.ArrayList; -import java.util.List; - -public class SectionEditor extends AbstractSectionEditor { - - public SectionEditor(Player player, String key, ItemSelector itemSelector, ConfigurationSection section) { - super(player, itemSelector, section, key); - } - - @Override - public List getItemList() { - ArrayList items = new ArrayList<>(); - items.add(new MaterialItem(this)); - items.add(new NickItem(this)); - items.add(new DisplayNameItem(this)); - items.add(new LoreItem(this)); - items.add(new CMDItem(this)); - items.add(new AmountItem(this)); - items.add(new TagItem(this)); - items.add(new UnbreakableItem(this)); - items.add(new DurabilityItem(this)); - items.add(new RandomDurabilityItem(this)); - items.add(new StackableItem(this)); - items.add(new PreventGrabItem(this)); - items.add(new PriceItem(this)); - items.add(new ShowInFinderItem(this)); - items.add(new DisableStatsItem(this)); - items.add(new DisableGameItem(this)); - items.add(new InstantGameItem(this)); - items.add(new ScoreItem(this)); - items.add(new SizeItem(this)); - items.add(new ItemFlagItem(this)); - items.add(new Head64Item(this)); - items.add(new NBTItem(this)); - items.add(new EnchantmentItem(this)); - items.add(new StoredEnchantmentItem(this)); - return items; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/AmountEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/AmountEditor.java deleted file mode 100644 index 6eca4aa1..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/AmountEditor.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -public class AmountEditor { - - private final SectionPage parentPage; - private String amount; - private final ConfigurationSection section; - - public AmountEditor(Player player, SectionPage parentPage) { - this.parentPage = parentPage; - this.section = parentPage.getSection(); - - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - var confirm = new ConfirmIcon(); - Gui upperGui = Gui.normal() - .setStructure("a # b") - .addIngredient('a', new ItemBuilder(Material.IRON_NUGGET).setDisplayName(String.valueOf(section.getInt("amount", 1)))) - .addIngredient('#', border) - .addIngredient('b', confirm) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', new ItemStack(Material.AIR)) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_TITLE_AMOUNT) - )) - .addRenameHandler(s -> { - amount = s; - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public class ConfirmIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (amount == null || amount.isEmpty()) { - return new ItemBuilder(Material.STRUCTURE_VOID).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DELETE_PROPERTY - ))); - } else { - try { - int m = Integer.parseInt(amount); - if (m >= 1) { - return new ItemBuilder(Material.IRON_NUGGET) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))) - .setAmount(m); - } else { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_INVALID_NUMBER - ))); - } - } catch (NumberFormatException e) { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_INVALID_NUMBER - ))); - } - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (amount == null || amount.isEmpty()) { - section.set("amount", null); - } else { - try { - int value = Integer.parseInt(amount); - if (value >= 1) { - section.set("amount", value); - } else { - return; - } - } catch (NumberFormatException e) { - return; - } - } - parentPage.reOpen(); - parentPage.save(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/CustomModelDataEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/CustomModelDataEditor.java deleted file mode 100644 index fcefd2e2..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/CustomModelDataEditor.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -public class CustomModelDataEditor { - - private final Player player; - private final SectionPage parentPage; - private String cmd; - private final ConfigurationSection section; - private final String material; - - public CustomModelDataEditor(Player player, SectionPage parentPage) { - this.player = player; - this.parentPage = parentPage; - this.section = parentPage.getSection(); - this.material = section.getString("material"); - - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - var confirm = new ConfirmIcon(); - Gui upperGui = Gui.normal() - .setStructure( - "a # b" - ) - .addIngredient('a', new ItemBuilder(CustomFishingPlugin.get() - .getItemManager() - .getItemStackAppearance(player, material) - ) - .setCustomModelData(section.getInt("custom-model-data", 0)) - .setDisplayName(String.valueOf(section.getInt("custom-model-data", 0)))) - .addIngredient('#', border) - .addIngredient('b', confirm) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', new ItemStack(Material.AIR)) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_TITLE_MODEL_DATA) - )) - .addRenameHandler(s -> { - cmd = s; - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public class ConfirmIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (cmd == null || cmd.isEmpty()) { - return new ItemBuilder(Material.STRUCTURE_VOID).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DELETE_PROPERTY - ))); - } else { - try { - int value = Integer.parseInt(cmd); - if (value >= 0) { - return new ItemBuilder( - CustomFishingPlugin.get() - .getItemManager() - .getItemStackAppearance(player, material) - ) - .setCustomModelData(value) - .setDisplayName(CFLocale.GUI_NEW_VALUE + value) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))); - } else { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_INVALID_NUMBER - ))); - } - } catch (NumberFormatException e) { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_INVALID_NUMBER - ))); - } - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (cmd == null || cmd.isEmpty()) { - section.set("custom-model-data", null); - } else { - try { - int value = Integer.parseInt(cmd); - if (value >= 0) { - section.set("custom-model-data", value); - } else { - return; - } - } catch (NumberFormatException e) { - return; - } - } - parentPage.reOpen(); - parentPage.save(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/DisplayNameEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/DisplayNameEditor.java deleted file mode 100644 index 5b8ce025..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/DisplayNameEditor.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -public class DisplayNameEditor { - - private final SectionPage parentPage; - private String name; - private final ConfigurationSection section; - - public DisplayNameEditor(Player player, SectionPage parentPage) { - this.parentPage = parentPage; - this.section = parentPage.getSection(); - - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - var confirm = new ConfirmIcon(); - Gui upperGui = Gui.normal() - .setStructure("a # b") - .addIngredient('a', new ItemBuilder(Material.NAME_TAG).setDisplayName(section.getString("display.name", CFLocale.GUI_NEW_DISPLAY_NAME))) - .addIngredient('#', border) - .addIngredient('b', confirm) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', new ItemStack(Material.AIR)) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_TITLE_DISPLAY_NAME) - )) - .addRenameHandler(s -> { - name = s; - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public class ConfirmIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (name == null || name.isEmpty()) { - return new ItemBuilder(Material.STRUCTURE_VOID).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DELETE_PROPERTY - ))); - } else { - return new ItemBuilder(Material.NAME_TAG) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "" + name - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))); - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (name == null || name.isEmpty()) { - section.set("display.name", null); - } else { - section.set("display.name", name); - } - parentPage.reOpen(); - parentPage.save(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/DurabilityEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/DurabilityEditor.java deleted file mode 100644 index 99ebfbc0..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/DurabilityEditor.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -public class DurabilityEditor { - - private final SectionPage parentPage; - private String dur; - private final ConfigurationSection section; - - public DurabilityEditor(Player player, SectionPage parentPage) { - this.parentPage = parentPage; - this.section = parentPage.getSection(); - - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - var confirm = new ConfirmIcon(); - Gui upperGui = Gui.normal() - .setStructure("a # b") - .addIngredient('a', new ItemBuilder(Material.NETHERITE_PICKAXE).setDisplayName(String.valueOf(section.getInt("max-durability", 64)))) - .addIngredient('#', border) - .addIngredient('b', confirm) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', new ItemStack(Material.AIR)) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_TITLE_CUSTOM_DURABILITY) - )) - .addRenameHandler(s -> { - dur = s; - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public class ConfirmIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (dur == null || dur.isEmpty()) { - return new ItemBuilder(Material.STRUCTURE_VOID).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DELETE_PROPERTY - ))); - } else { - try { - int m = Integer.parseInt(dur); - if (m >= 1) { - return new ItemBuilder(Material.NETHERITE_PICKAXE) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NEW_VALUE + dur - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))) - .setDamage(Math.max(0, Material.NETHERITE_PICKAXE.getMaxDurability() - m)); - } else { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_INVALID_NUMBER - ))); - } - } catch (NumberFormatException e) { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_INVALID_NUMBER - ))); - } - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (dur == null || dur.isEmpty()) { - section.set("max-durability", null); - } else { - try { - int value = Integer.parseInt(dur); - if (value >= 1) { - section.set("max-durability", value); - } else { - return; - } - } catch (NumberFormatException e) { - return; - } - } - parentPage.reOpen(); - parentPage.save(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/EnchantmentEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/EnchantmentEditor.java deleted file mode 100644 index cc954eac..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/EnchantmentEditor.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.gui.structure.Markers; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class EnchantmentEditor { - - private final Player player; - private final SectionPage parentPage; - private final ArrayList enchantments; - private final ConfigurationSection section; - private int index; - private final boolean store; - - public EnchantmentEditor(Player player, SectionPage parentPage, boolean store) { - this.player = player; - this.parentPage = parentPage; - this.section = parentPage.getSection(); - this.store = store; - this.index = 0; - this.enchantments = new ArrayList<>(); - this.enchantments.add(CFLocale.GUI_SELECT_ONE_ENCHANTMENT); - ConfigurationSection eSection = section.getConfigurationSection(store ? "stored-enchantments" : "enchantments"); - if (eSection != null) - for (Map.Entry entry : eSection.getValues(false).entrySet()) { - this.enchantments.add(entry.getKey() + ":" + entry.getValue()); - } - reOpen(0); - } - - public void reOpen(int idx) { - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - var confirm = new ConfirmIcon(); - Gui upperGui = Gui.normal() - .setStructure("a # b") - .addIngredient('a', new ItemBuilder(Material.NAME_TAG).setDisplayName(enchantments.get(idx))) - .addIngredient('#', border) - .addIngredient('b', confirm) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .setContent(getContents()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(store ? CFLocale.GUI_TITLE_STORED_ENCHANTMENT : CFLocale.GUI_TITLE_ENCHANTMENT) - )) - .addRenameHandler(s -> { - if (index == 0) return; - enchantments.set(index, s); - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public List getContents() { - ArrayList items = new ArrayList<>(); - int i = 1; - List subList = enchantments.subList(1, enchantments.size()); - for (String lore : subList) { - items.add(new EnchantmentElement(lore, i++)); - } - items.add(new AddEnchantment()); - return items; - } - - public class AddEnchantment extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.ANVIL).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ADD_NEW_ENCHANTMENT - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - enchantments.add("namespace:enchantment:level"); - index = enchantments.size() - 1; - reOpen(index); - } - } - - public class EnchantmentElement extends AbstractItem { - - private final String line; - private final int idx; - - public EnchantmentElement(String line, int idx) { - this.line = line; - this.idx = idx; - } - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.ENCHANTED_BOOK).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - line - ))).addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_DELETE - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType == ClickType.LEFT) { - index = idx; - reOpen(idx); - } else if (clickType == ClickType.RIGHT) { - enchantments.remove(idx); - index = Math.min(index, enchantments.size() - 1); - reOpen(index); - } - } - } - - public class ConfirmIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - List subList = enchantments.subList(1, enchantments.size()); - if (subList.isEmpty()) { - return new ItemBuilder(Material.STRUCTURE_VOID).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DELETE_PROPERTY - ))); - } else { - var builder = new ItemBuilder(Material.NAME_TAG) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))); - for (String enchantment : subList) { - String[] split = enchantment.split(":"); - if (split.length != 3) { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ILLEGAL_FORMAT - ))); - } - try { - Integer.parseInt(split[2]); - builder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - " - " + enchantment - ))); - } catch (NumberFormatException e) { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ILLEGAL_FORMAT - ))); - } - } - return builder; - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - List subList = enchantments.subList(1, enchantments.size()); - for (String line : subList) { - String[] split = line.split(":"); - if (split.length != 3) { - return; - } - try { - Integer.parseInt(split[2]); - } catch (NumberFormatException e) { - return; - } - } - section.set(store ? "stored-enchantments" : "enchantments", null); - for (String line : subList) { - String[] split = line.split(":"); - section.set((store ? "stored-enchantments" : "enchantments") + "." + split[0] + ":" + split[1], Integer.parseInt(split[2])); - } - parentPage.reOpen(); - parentPage.save(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/ItemFlagEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/ItemFlagEditor.java deleted file mode 100644 index 5f6d1e7d..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/ItemFlagEditor.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemFlag; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.gui.structure.Markers; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -import java.util.ArrayList; -import java.util.List; - -public class ItemFlagEditor { - - private final Player player; - private final SectionPage parentPage; - private final List flags; - private final ConfigurationSection section; - - public ItemFlagEditor(Player player, SectionPage parentPage) { - this.player = player; - this.parentPage = parentPage; - this.section = parentPage.getSection(); - this.flags = section.getStringList("item-flags"); - reOpen(); - } - - public void reOpen() { - Gui upperGui = Gui.normal() - .setStructure( - "# a #" - ) - .addIngredient('a', new ItemBuilder(CustomFishingPlugin.get().getItemManager().getItemBuilder(section, "item", "id").build(player))) - .addIngredient('#', new SimpleItem(new ItemBuilder(Material.AIR))) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .setContent(getContents()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_TITLE_ITEM_FLAG) - )) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public List getContents() { - ArrayList items = new ArrayList<>(); - for (ItemFlag itemFlag : ItemFlag.values()) { - items.add(new ItemFlagToggleItem(itemFlag.name())); - } - return items; - } - - public class ItemFlagToggleItem extends AbstractItem { - - private final String flag; - - public ItemFlagToggleItem(String flag) { - this.flag = flag; - } - - @Override - public ItemProvider getItemProvider() { - if (flags.contains(flag)) { - return new ItemBuilder(Material.GREEN_BANNER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "" + flag - ))); - } else { - return new ItemBuilder(Material.RED_BANNER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "" + flag - ))); - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (flags.contains(flag)) { - flags.remove(flag); - } else { - flags.add(flag); - } - if (flags.size() != 0) { - section.set("item-flags", flags); - } else { - section.set("item-flags", null); - } - parentPage.save(); - reOpen(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/LoreEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/LoreEditor.java deleted file mode 100644 index dd9aa32f..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/LoreEditor.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.gui.structure.Markers; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -import java.util.ArrayList; -import java.util.List; - -public class LoreEditor { - - private final Player player; - private final SectionPage parentPage; - private final ArrayList lore; - private final ConfigurationSection section; - private int index; - - public LoreEditor(Player player, SectionPage parentPage) { - this.player = player; - this.parentPage = parentPage; - this.section = parentPage.getSection(); - this.index = 0; - this.lore = new ArrayList<>(section.getStringList("display.lore")); - this.lore.add(0, CFLocale.GUI_SELECT_ONE_LORE); - reOpen(0); - } - - public void reOpen(int idx) { - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - var confirm = new ConfirmIcon(); - Gui upperGui = Gui.normal() - .setStructure("a # b") - .addIngredient('a', new ItemBuilder(Material.NAME_TAG).setDisplayName(lore.get(idx))) - .addIngredient('#', border) - .addIngredient('b', confirm) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .setContent(getContents()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_TITLE_LORE) - )) - .addRenameHandler(s -> { - if (index == 0) return; - lore.set(index, s); - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public List getContents() { - ArrayList items = new ArrayList<>(); - int i = 1; - List subList = lore.subList(1, lore.size()); - for (String lore : subList) { - items.add(new LoreElement(lore, i++)); - } - items.add(new AddLore()); - return items; - } - - public class AddLore extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.ANVIL).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ADD_NEW_LORE - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - lore.add("Text"); - index = lore.size() - 1; - reOpen(index); - } - } - - public class LoreElement extends AbstractItem { - - private final String line; - private final int idx; - - public LoreElement(String line, int idx) { - this.line = line; - this.idx = idx; - } - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.PAPER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - line - ))).addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_DELETE - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType == ClickType.LEFT) { - index = idx; - reOpen(idx); - } else if (clickType == ClickType.RIGHT) { - lore.remove(idx); - index = Math.min(index, lore.size() - 1); - reOpen(index); - } - } - } - - public class ConfirmIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - List subList = lore.subList(1, lore.size()); - if (subList.isEmpty()) { - return new ItemBuilder(Material.STRUCTURE_VOID).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DELETE_PROPERTY - ))); - } else { - var builder = new ItemBuilder(Material.NAME_TAG) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))); - for (String lore : subList) { - builder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - " - " + lore - ))); - } - return builder; - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - List subList = lore.subList(1, lore.size()); - if (lore.isEmpty()) { - section.set("display.lore", null); - } else { - section.set("display.lore", subList); - } - parentPage.reOpen(); - parentPage.save(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/MaterialEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/MaterialEditor.java deleted file mode 100644 index f5dfdf87..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/MaterialEditor.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.mechanic.item.ItemManagerImpl; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.gui.structure.Markers; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -import java.util.ArrayList; -import java.util.List; - -public class MaterialEditor { - - private final Player player; - private final SectionPage parentPage; - private String material; - private final ConfigurationSection section; - - public MaterialEditor(Player player, SectionPage parentPage) { - this.player = player; - this.parentPage = parentPage; - this.section = parentPage.getSection(); - this.material = section.getString("material"); - - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - var confirm = new ConfirmIcon(); - var itemBuilder = new ItemBuilder(CustomFishingPlugin.get() - .getItemManager() - .getItemStackAppearance(player, material) - ) - .setDisplayName(section.getString("material", "")); - - if (section.contains("custom-model-data")) - itemBuilder.setCustomModelData(section.getInt("custom-model-data", 0)); - - Gui upperGui = Gui.normal() - .setStructure("a # b") - .addIngredient('a', itemBuilder) - .addIngredient('#', border) - .addIngredient('b', confirm) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .setContent(getCompatibilityItemList()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_TITLE_MATERIAL) - )) - .addRenameHandler(s -> { - material = s; - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public List getCompatibilityItemList() { - ArrayList items = new ArrayList<>(); - for (String lib : ((ItemManagerImpl) CustomFishingPlugin.get().getItemManager()).getItemLibraries()) { - switch (lib) { - case "MMOItems" -> items.add(new SimpleItem(new ItemBuilder(Material.BELL).setDisplayName(lib + ":TYPE:ID"))); - case "ItemsAdder" -> items.add(new SimpleItem(new ItemBuilder(Material.BELL).setDisplayName(lib + ":namespace:id"))); - case "vanilla", "CustomFishing" -> {} - default -> items.add(new SimpleItem(new ItemBuilder(Material.BELL).setDisplayName(lib + ":ID"))); - } - } - return items; - } - - public class ConfirmIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (material == null || material.isEmpty()) { - return new ItemBuilder(Material.STRUCTURE_VOID).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DELETE_PROPERTY - ))); - } else { - var builder = new ItemBuilder( - CustomFishingPlugin.get() - .getItemManager() - .getItemStackAppearance(player, material) - ).setDisplayName(CFLocale.GUI_NEW_VALUE + material) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))); - if (section.contains("custom-model-data")) - builder.setCustomModelData(section.getInt("custom-model-data")); - return builder; - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (material == null || material.isEmpty()) { - section.set("material", null); - } else if (CustomFishingPlugin.get().getItemManager().getItemStackAppearance(player, material).getType() == Material.BARRIER) { - return; - } else { - section.set("material", material); - } - parentPage.reOpen(); - parentPage.save(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/NBTEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/NBTEditor.java deleted file mode 100644 index 9f044ca5..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/NBTEditor.java +++ /dev/null @@ -1,699 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.setting.CFLocale; -import net.momirealms.customfishing.util.ConfigUtils; -import net.momirealms.customfishing.util.NBTUtils; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.gui.structure.Markers; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -import java.util.*; - -@SuppressWarnings("DuplicatedCode") -public class NBTEditor { - - private final Player player; - private final SectionPage parentPage; - private ConfigurationSection nbtSection; - private ConfigurationSection currentSection; - private String value; - private String currentNode; - - public NBTEditor(Player player, SectionPage parentPage) { - this.player = player; - this.parentPage = parentPage; - this.nbtSection = parentPage.getSection().getConfigurationSection("nbt"); - if (this.nbtSection == null) - this.nbtSection = parentPage.getSection().createSection("nbt"); - this.currentSection = nbtSection; - this.currentNode = ""; - reOpenMain(); - } - - public List getNBTContents() { - Deque deque = new ArrayDeque<>(); - for (Map.Entry entry : currentSection.getValues(false).entrySet()) { - String path = Objects.equals(currentNode, "") ? entry.getKey() : currentNode + "." + entry.getKey(); - if (entry.getValue() instanceof List list) { - deque.addLast(new InvListIcon(path)); - } else if (entry.getValue() instanceof String str) { - deque.addLast(new InvValueIcon(path, str)); - } else if (entry.getValue() instanceof ConfigurationSection inner) { - deque.addFirst(new InvCompoundIcon(path, inner)); - } else if (entry.getValue() instanceof Map map) { - deque.addLast(new InvMapIcon(path)); - } - } - deque.addLast(new NewCompoundIcon()); - deque.addLast(new NewListIcon()); - deque.addLast(new NewValueIcon()); - if (currentSection.getParent() != null && !currentSection.getName().equals("nbt")) { - deque.addLast(new BackToParentIcon()); - } - return new ArrayList<>(deque); - } - - public void reOpenMain() { - Gui upperGui = Gui.normal() - .setStructure("b b c") - .addIngredient('b', new ItemStack(Material.AIR)) - .addIngredient('c', new SaveIcon()) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .setContent(getNBTContents()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_NBT_EDIT_TITLE))) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public void reOpenAddCompound() { - var confirm = new ConfirmCompoundItem(); - Gui upperGui = Gui.normal() - .setStructure("a b c") - .addIngredient('a', new ItemBuilder(Material.COMMAND_BLOCK_MINECART).setDisplayName("")) - .addIngredient('b', new ItemStack(Material.AIR)) - .addIngredient('c', confirm) - .build(); - - value = ""; - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .setContent(getNBTContents()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_TITLE_NBT_COMPOUND) - )) - .addRenameHandler(s -> { - value = s; - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public void reOpenAddList() { - var confirm = new ConfirmListItem(); - Gui upperGui = Gui.normal() - .setStructure("a b c") - .addIngredient('a', new ItemBuilder(Material.CHAIN_COMMAND_BLOCK).setDisplayName("")) - .addIngredient('b', new ItemStack(Material.AIR)) - .addIngredient('c', confirm) - .build(); - - value = ""; - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .setContent(getNBTContents()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_TITLE_NBT_LIST) - )) - .addRenameHandler(s -> { - value = s; - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public void reOpenAddValue() { - var confirm =new ConfirmValueItem(); - Gui upperGui = Gui.normal() - .setStructure("a b c") - .addIngredient('a', new ItemBuilder(Material.COMMAND_BLOCK).setDisplayName("")) - .addIngredient('b', new ItemStack(Material.AIR)) - .addIngredient('c', confirm) - .build(); - - value = ""; - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .setContent(getNBTContents()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_TITLE_NBT_KEY))) - .addRenameHandler(s -> { - value = s; - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public void reOpenSetValue(String key, String type) { - var save = new SaveValueIcon(key); - Gui upperGui = Gui.normal() - .setStructure("a b c") - .addIngredient('a', new ItemBuilder(Material.COMMAND_BLOCK).setDisplayName(type == null ? "" : "(" + type + ") ")) - .addIngredient('b', new ItemStack(Material.AIR)) - .addIngredient('c', save) - .build(); - - value = ""; - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .setContent(getTypeContents(key)) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_NBT_SET_VALUE_TITLE) - )) - .addRenameHandler(s -> { - value = s; - save.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public void removeByNode(String node) { - nbtSection.set(node, null); - parentPage.save(); - } - - public List getTypeContents(String key) { - ArrayList list = new ArrayList<>(); - for (Map.Entry entry : Map.of( - "String","some text", - "Byte","1", - "Short","123", - "Int","123456", - "Long","123456789", - "Double", "1.2345", - "Float", "1.23", - "Boolean", "true", - "IntArray", "[111,222,333,444]", - "ByteArray","[1,2,3,4]" - ).entrySet()) { - list.add(new TypeItem(key, entry.getKey(), entry.getValue())); - } - return list; - } - - public class TypeItem extends AbstractItem { - - private final String type; - private final String tip; - private final String key; - - public TypeItem(String key, String type, String tip) { - this.type = type; - this.tip = tip; - this.key = key; - } - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.BELL).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "(" + type + ") " + tip - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - reOpenSetValue(key, type); - } - } - - public class ConfirmCompoundItem extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (value == null || value.equals("") || value.contains(".") || currentSection.contains(value)) { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NBT_INVALID_KEY - ))); - } - - return new ItemBuilder(Material.COMMAND_BLOCK_MINECART).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NEW_VALUE + value - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_CANCEL - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - if (value == null || value.equals("") || value.contains(".")) { - return; - } - if (currentSection.contains(value)) { - return; - } - currentSection.createSection(value); - parentPage.save(); - } - reOpenMain(); - } - } - - public class ConfirmListItem extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (value == null || value.equals("") || value.contains(".") || currentSection.contains(value)) { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NBT_INVALID_KEY - ))); - } - return new ItemBuilder(Material.CHAIN_COMMAND_BLOCK).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NEW_VALUE + value - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_CANCEL - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - if (value == null || value.equals("") || value.contains(".")) { - return; - } - if (currentSection.contains(value)) { - return; - } - currentSection.set(value, new ArrayList<>()); - parentPage.save(); - } - reOpenMain(); - } - } - - public class ConfirmValueItem extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (value == null || value.equals("") || value.contains(".") || currentSection.contains(value)) { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NBT_INVALID_KEY - ))); - } - - return new ItemBuilder(Material.COMMAND_BLOCK).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NEW_VALUE + value - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_CANCEL - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - if (value == null || value.equals("") || value.contains(".")) { - return; - } - if (currentSection.contains(value)) { - return; - } - reOpenSetValue(value, null); - } else if (clickType.isRightClick()) { - reOpenMain(); - } - } - } - - public class NewCompoundIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.OAK_SIGN).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NBT_ADD_COMPOUND - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - reOpenAddCompound(); - } - } - - public class NewListIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.SPRUCE_SIGN).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NBT_ADD_LIST - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - reOpenAddList(); - } - } - - public class NewValueIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.ACACIA_SIGN).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NBT_ADD_VALUE - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - reOpenAddValue(); - } - } - - public class InvCompoundIcon extends AbstractItem { - - private final String node; - private final ConfigurationSection compound; - - public InvCompoundIcon(String node, ConfigurationSection compound) { - this.compound = compound; - this.node = node; - } - - @Override - public ItemProvider getItemProvider() { - String[] splits = node.split("\\."); - return new ItemBuilder(Material.COMMAND_BLOCK_MINECART).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "Compound: " + splits[splits.length -1] - ))).addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_DELETE - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isLeftClick()) { - currentSection = compound; - currentNode = node; - } else if (clickType.isRightClick()) { - removeByNode(node); - } - reOpenMain(); - } - } - - public class InvValueIcon extends AbstractItem { - - private final String node; - private final String value; - - public InvValueIcon(String node, String value) { - this.node = node; - this.value = value; - } - - @Override - public ItemProvider getItemProvider() { - String[] splits = node.split("\\."); - return new ItemBuilder(Material.REPEATING_COMMAND_BLOCK).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - splits[splits.length -1] + ": " + value - ))) - .addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_LEFT_CLICK_EDIT - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_DELETE - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - String[] split = node.split("\\."); - if (clickType.isLeftClick()) { - reOpenSetValue(split[split.length-1], NBTUtils.getTypeAndData(value)[0]); - } else if (clickType.isRightClick()) { - removeByNode(node); - reOpenMain(); - } - } - } - - public class InvListIcon extends AbstractItem { - - private final String node; - - public InvListIcon(String node) { - this.node = node; - } - - @Override - public ItemProvider getItemProvider() { - String[] splits = node.split("\\."); - return new ItemBuilder(Material.CHAIN_COMMAND_BLOCK).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "List: " + splits[splits.length -1] - ))) - .addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "" + CFLocale.GUI_LEFT_CLICK_EDIT + "" - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_DELETE - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isRightClick()) { - removeByNode(node); - reOpenMain(); - } - } - } - - public class InvMapIcon extends AbstractItem { - - private final String node; - - public InvMapIcon(String node) { - this.node = node; - } - - @Override - public ItemProvider getItemProvider() { - String[] splits = node.split("\\."); - return new ItemBuilder(Material.COMMAND_BLOCK).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "Map: " + splits[splits.length -1] - ))) - .addLoreLines("") - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "" + CFLocale.GUI_LEFT_CLICK_EDIT + "" - ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_DELETE - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType.isRightClick()) { - removeByNode(node); - reOpenMain(); - } - } - } - - public class SaveValueIcon extends AbstractItem { - - private final String key; - - public SaveValueIcon(String key) { - this.key = key; - } - - @Override - public ItemProvider getItemProvider() { - try { - NBTUtils.getTypeAndData(value); - return new ItemBuilder(Material.COMMAND_BLOCK) - .setDisplayName(value) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_RIGHT_CLICK_CANCEL - ))); - } catch (IllegalArgumentException e) { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ILLEGAL_FORMAT - ))); - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (clickType == ClickType.LEFT) { - try { - NBTUtils.getTypeAndData(value); - currentSection.set(key, value); - parentPage.save(); - reOpenMain(); - } catch (IllegalArgumentException e) { - reOpenMain(); - } - } else if (clickType == ClickType.RIGHT) { - reOpenMain(); - } - } - } - - public class SaveIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (nbtSection.getValues(false).size() > 0) { - var builder = new ItemBuilder(Material.ACACIA_SIGN).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NBT_PREVIEW - ))); - for (String line : ConfigUtils.getReadableSection(nbtSection.getValues(false))) { - builder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - line - ))); - } - return builder; - } else { - return new ItemBuilder(Material.STRUCTURE_VOID).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DELETE_PROPERTY - ))); - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (nbtSection.getValues(false).size() == 0) { - Objects.requireNonNull(nbtSection.getParent()).set("nbt", null); - } - parentPage.save(); - parentPage.reOpen(); - } - } - - public class BackToParentIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.MINECART).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NBT_BACK_TO_COMPOUND - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - currentSection = currentSection.getParent(); - currentNode = currentNode.lastIndexOf(".") == -1 ? "" : currentNode.substring(0, currentNode.lastIndexOf(".")); - reOpenMain(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/NickEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/NickEditor.java deleted file mode 100644 index 77ece67f..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/NickEditor.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -public class NickEditor { - - private final SectionPage parentPage; - private String nick; - private final ConfigurationSection section; - - public NickEditor(Player player, SectionPage parentPage) { - this.parentPage = parentPage; - this.section = parentPage.getSection(); - - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - var confirm = new ConfirmIcon(); - Gui upperGui = Gui.normal() - .setStructure("a # b") - .addIngredient('a', new ItemBuilder(Material.WRITABLE_BOOK).setDisplayName(section.getString("nick", CFLocale.GUI_NICK_NEW))) - .addIngredient('#', border) - .addIngredient('b', confirm) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', new ItemStack(Material.AIR)) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_NICK_TITLE) - )) - .addRenameHandler(s -> { - nick = s; - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public class ConfirmIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (nick == null || nick.isEmpty()) { - return new ItemBuilder(Material.STRUCTURE_VOID).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DELETE_PROPERTY - ))); - } else { - return new ItemBuilder(Material.WRITABLE_BOOK) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - "" + nick - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))); - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (nick == null || nick.isEmpty()) { - section.set("nick", null); - } else { - section.set("nick", nick); - } - parentPage.reOpen(); - parentPage.save(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/PriceEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/PriceEditor.java deleted file mode 100644 index aa866830..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/PriceEditor.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -public class PriceEditor { - - private final Player player; - private final SectionPage parentPage; - private final String[] price; - private int index; - private final ConfigurationSection section; - - public PriceEditor(Player player, SectionPage parentPage) { - this.player = player; - this.parentPage = parentPage; - this.section = parentPage.getSection(); - this.index = 0; - this.price = new String[]{section.getString("price.base","0"), section.getString("price.bonus","0")}; - reOpen(); - } - - public void reOpen() { - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - var confirm = new ConfirmIcon(); - Gui upperGui = Gui.normal() - .setStructure("a # b") - .addIngredient('a', new ItemBuilder(Material.GOLD_INGOT).setDisplayName(price[index])) - .addIngredient('#', border) - .addIngredient('b', confirm) - .build(); - - var gui = PagedGui.items() - .setStructure( - "a b x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', new ItemStack(Material.AIR)) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .addIngredient('a', new BaseItem()) - .addIngredient('b', new BonusItem()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_PRICE_TITLE) - )) - .addRenameHandler(s -> { - if (s == null || s.equals("")) { - price[index] = "0"; - return; - } - price[index] = s; - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public class BaseItem extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.GOLD_BLOCK).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_PRICE_BASE - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - index = 0; - reOpen(); - } - } - - public class BonusItem extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.GOLD_NUGGET).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_PRICE_BONUS - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - index = 1; - reOpen(); - } - } - - public class ConfirmIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (price[0].equals("0") && price[1].equals("0")) { - return new ItemBuilder(Material.STRUCTURE_VOID).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DELETE_PROPERTY - ))); - } else { - try { - Double.parseDouble(price[0]); - Double.parseDouble(price[1]); - return new ItemBuilder(Material.GOLD_INGOT) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NEW_VALUE - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_PRICE_BASE + price[0] - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_ITEM_PRICE_BONUS + price[1] - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))); - } catch (NumberFormatException e) { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_INVALID_NUMBER - ))); - } - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (price[0].equals("0") && price[1].equals("0")) { - section.set("price", null); - } else { - try { - double base = Double.parseDouble(price[0]); - double bonus = Double.parseDouble(price[1]); - if (base != 0) { - section.set("price.base", base); - } - if (bonus != 0) { - section.set("price.bonus", bonus); - } - } catch (NumberFormatException e) { - return; - } - } - parentPage.reOpen(); - parentPage.save(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/ScoreEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/ScoreEditor.java deleted file mode 100644 index c524d29f..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/ScoreEditor.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -public class ScoreEditor { - - private final SectionPage parentPage; - private String score; - private final ConfigurationSection section; - - public ScoreEditor(Player player, SectionPage parentPage) { - this.parentPage = parentPage; - this.section = parentPage.getSection(); - - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - var confirm = new ConfirmIcon(); - Gui upperGui = Gui.normal() - .setStructure("a # b") - .addIngredient('a', new ItemBuilder(Material.NETHER_STAR).setDisplayName(String.valueOf(section.getDouble("score", 0)))) - .addIngredient('#', border) - .addIngredient('b', confirm) - .build(); - - var gui = PagedGui.items() - .setStructure( - "x x x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', new ItemStack(Material.AIR)) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_SCORE_TITLE) - )) - .addRenameHandler(s -> { - score = s; - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public class ConfirmIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (score == null || score.isEmpty()) { - return new ItemBuilder(Material.STRUCTURE_VOID).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DELETE_PROPERTY - ))); - } else { - try { - Double.parseDouble(score); - return new ItemBuilder(Material.NETHER_STAR) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NEW_VALUE + score - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))); - } catch (NumberFormatException e) { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_INVALID_NUMBER - ))); - } - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (score == null || score.isEmpty()) { - section.set("score", null); - } else { - try { - double value = Double.parseDouble(score); - if (value >= 0) { - section.set("score", value); - } else { - return; - } - } catch (NumberFormatException e) { - return; - } - } - parentPage.reOpen(); - parentPage.save(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/SizeEditor.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/SizeEditor.java deleted file mode 100644 index 8ee058b8..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/property/SizeEditor.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.gui.page.property; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.adventure.component.ShadedAdventureComponentWrapper; -import net.momirealms.customfishing.gui.SectionPage; -import net.momirealms.customfishing.gui.icon.BackGroundItem; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.PagedGui; -import xyz.xenondevs.invui.item.Item; -import xyz.xenondevs.invui.item.ItemProvider; -import xyz.xenondevs.invui.item.builder.ItemBuilder; -import xyz.xenondevs.invui.item.impl.AbstractItem; -import xyz.xenondevs.invui.item.impl.SimpleItem; -import xyz.xenondevs.invui.window.AnvilWindow; - -public class SizeEditor { - - private final Player player; - private final SectionPage parentPage; - private final String[] size; - private int index; - private final ConfigurationSection section; - - public SizeEditor(Player player, SectionPage parentPage) { - this.player = player; - this.parentPage = parentPage; - this.section = parentPage.getSection(); - this.index = 0; - this.size = section.contains("size") ? section.getString("size").split("~") : new String[]{"0","0"}; - reOpen(); - } - - public void reOpen() { - Item border = new SimpleItem(new ItemBuilder(Material.AIR)); - var confirm = new ConfirmIcon(); - Gui upperGui = Gui.normal() - .setStructure("a # b") - .addIngredient('a', new ItemBuilder(Material.PUFFERFISH).setDisplayName(size[index])) - .addIngredient('#', border) - .addIngredient('b', confirm) - .build(); - - var gui = PagedGui.items() - .setStructure( - "a b x x x x x x x", - "x x x x x x x x x", - "x x x x x x x x x", - "# # # # c # # # #" - ) - .addIngredient('x', new ItemStack(Material.AIR)) - .addIngredient('c', parentPage.getBackItem()) - .addIngredient('#', new BackGroundItem()) - .addIngredient('a', new MinItem()) - .addIngredient('b', new MaxItem()) - .build(); - - var window = AnvilWindow.split() - .setViewer(player) - .setTitle(new ShadedAdventureComponentWrapper( - AdventureHelper.getInstance().getComponentFromMiniMessage(CFLocale.GUI_SIZE_TITLE) - )) - .addRenameHandler(s -> { - if (s == null || s.equals("")) { - size[index] = "0"; - return; - } - size[index] = s; - confirm.notifyWindows(); - }) - .setUpperGui(upperGui) - .setLowerGui(gui) - .build(); - - window.open(); - } - - public class MinItem extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.IRON_INGOT).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_SIZE_MIN - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - index = 0; - reOpen(); - } - } - - public class MaxItem extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - return new ItemBuilder(Material.IRON_BLOCK).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_SIZE_MAX - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - index = 1; - reOpen(); - } - } - - public class ConfirmIcon extends AbstractItem { - - @Override - public ItemProvider getItemProvider() { - if (size[0].equals("0") && size[1].equals("0")) { - return new ItemBuilder(Material.STRUCTURE_VOID).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_DELETE_PROPERTY - ))); - } else { - try { - double min = Double.parseDouble(size[0]); - double max = Double.parseDouble(size[1]); - - if (min <= max) { - return new ItemBuilder(Material.PUFFERFISH) - .setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_NEW_VALUE - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - " - " + size[0] + "~" + size[1] - ))) - .addLoreLines(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_CLICK_CONFIRM - ))); - } else { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_SIZE_MAX_NO_LESS - ))); - } - } catch (NumberFormatException e) { - return new ItemBuilder(Material.BARRIER).setDisplayName(new ShadedAdventureComponentWrapper(AdventureHelper.getInstance().getComponentFromMiniMessage( - CFLocale.GUI_INVALID_NUMBER - ))); - } - } - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { - if (size[0].equals("0") && size[1].equals("0")) { - section.set("size", null); - } else { - try { - double min = Double.parseDouble(size[0]); - double max = Double.parseDouble(size[1]); - if (min <= max) { - section.set("size", min + "~" + max); - } - } catch (NumberFormatException e) { - return; - } - } - parentPage.reOpen(); - parentPage.save(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/Dependency.java deleted file mode 100644 index d8321eec..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/dependencies/Dependency.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.momirealms.customfishing.libraries.dependencies; - -import com.google.common.collect.ImmutableList; -import net.momirealms.customfishing.libraries.dependencies.relocation.Relocation; -import org.bukkit.Bukkit; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.List; -import java.util.Locale; - -/** - * The dependencies used by CustomFishing. - */ -public enum Dependency { - - ASM( - "org.ow2.asm", - "asm", - "9.7", - "asm" - ), - ASM_COMMONS( - "org.ow2.asm", - "asm-commons", - "9.7", - "asm-commons" - ), - JAR_RELOCATOR( - "me.lucko", - "jar-relocator", - "1.7", - "jar-relocator" - ), - COMMAND_API( - "dev{}jorel", - "commandapi-bukkit-shade", - "9.4.1", - "commandapi-bukkit", - Relocation.of("commandapi", "dev{}jorel{}commandapi") - ), - COMMAND_API_MOJMAP( - "dev{}jorel", - "commandapi-bukkit-shade-mojang-mapped", - "9.4.1", - "commandapi-bukkit-shade-mojang-mapped", - Relocation.of("commandapi", "dev{}jorel{}commandapi") - ), - MARIADB_DRIVER( - "org{}mariadb{}jdbc", - "mariadb-java-client", - "3.3.3", - "mariadb-java-client", - Relocation.of("mariadb", "org{}mariadb") - ), - BOOSTED_YAML( - "dev{}dejvokep", - "boosted-yaml", - "1.3.4", - "boosted-yaml", - Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml") - ), - EXP4J( - "net{}objecthunter", - "exp4j", - "0.4.8", - "exp4j", - Relocation.of("exp4j", "net{}objecthunter{}exp4j") - ), - MYSQL_DRIVER( - "com{}mysql", - "mysql-connector-j", - "8.4.0", - "mysql-connector-j", - Relocation.of("mysql", "com{}mysql") - ), - H2_DRIVER( - "com.h2database", - "h2", - "2.2.224", - "h2database" - ), - SQLITE_DRIVER( - "org.xerial", - "sqlite-jdbc", - "3.45.3.0", - "sqlite-jdbc" - ), - HIKARI( - "com{}zaxxer", - "HikariCP", - "5.1.0", - "HikariCP", - Relocation.of("hikari", "com{}zaxxer{}hikari") - ), - SLF4J_SIMPLE( - "org.slf4j", - "slf4j-simple", - "2.0.12", - "slf4j-simple" - ), - SLF4J_API( - "org.slf4j", - "slf4j-api", - "2.0.12", - "slf4j-api" - ), - MONGODB_DRIVER_CORE( - "org{}mongodb", - "mongodb-driver-core", - "5.1.0", - "mongodb-driver-core", - Relocation.of("mongodb", "com{}mongodb"), - Relocation.of("bson", "org{}bson") - ), - MONGODB_DRIVER_SYNC( - "org{}mongodb", - "mongodb-driver-sync", - "5.1.0", - "mongodb-driver-sync", - Relocation.of("mongodb", "com{}mongodb"), - Relocation.of("bson", "org{}bson") - ), - MONGODB_DRIVER_BSON( - "org{}mongodb", - "bson", - "5.1.0", - "mongodb-bson", - Relocation.of("mongodb", "com{}mongodb"), - Relocation.of("bson", "org{}bson") - ), - JEDIS( - "redis{}clients", - "jedis", - "5.1.2", - "jedis", - Relocation.of("jedis", "redis{}clients{}jedis"), - Relocation.of("commonspool2", "org{}apache{}commons{}pool2") - ), - BSTATS_BASE( - "org{}bstats", - "bstats-base", - "3.0.2", - "bstats-base", - Relocation.of("bstats", "org{}bstats") - ), - BSTATS_BUKKIT( - "org{}bstats", - "bstats-bukkit", - "3.0.2", - "bstats-bukkit", - Relocation.of("bstats", "org{}bstats") - ), - COMMONS_POOL_2( - "org{}apache{}commons", - "commons-pool2", - "2.12.0", - "commons-pool2", - Relocation.of("commonspool2", "org{}apache{}commons{}pool2") - ), - GSON( - "com.google.code.gson", - "gson", - "2.10.1", - "gson" - ), - COMMONS_LANG_3( - "org{}apache{}commons", - "commons-lang3", - "3.14.0", - "commons-lang3", - Relocation.of("lang3", "org{}apache{}commons{}lang3") - ); - - private final String mavenRepoPath; - private final String version; - private final List relocations; - private final String artifact; - - private static final String MAVEN_FORMAT = "%s/%s/%s/%s-%s.jar"; - - Dependency(String groupId, String artifactId, String version, String artifact) { - this(groupId, artifactId, version, artifact, new Relocation[0]); - } - - Dependency(String groupId, String artifactId, String version, String artifact, Relocation... relocations) { - this.mavenRepoPath = String.format(MAVEN_FORMAT, - rewriteEscaping(groupId).replace(".", "/"), - rewriteEscaping(artifactId), - version, - rewriteEscaping(artifactId), - version - ); - this.version = version; - this.relocations = ImmutableList.copyOf(relocations); - this.artifact = artifact; - } - - private static String rewriteEscaping(String s) { - return s.replace("{}", "."); - } - - public String getFileName(String classifier) { - String name = artifact.toLowerCase(Locale.ROOT).replace('_', '-'); - String extra = classifier == null || classifier.isEmpty() - ? "" - : "-" + classifier; - - return name + "-" + this.version + extra + ".jar"; - } - - String getMavenRepoPath() { - return this.mavenRepoPath; - } - - public List getRelocations() { - return this.relocations; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/loader/JarInJarClassLoader.java b/plugin/src/main/java/net/momirealms/customfishing/libraries/loader/JarInJarClassLoader.java deleted file mode 100644 index e61b6c76..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/loader/JarInJarClassLoader.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.momirealms.customfishing.libraries.loader; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; - -/** - * Classloader that can load a jar from within another jar file. - * - *

The "loader" jar contains the loading code & public API classes, - * and is class-loaded by the platform.

- * - *

The inner "plugin" jar contains the plugin itself, and is class-loaded - * by the loading code & this classloader.

- */ -public class JarInJarClassLoader extends URLClassLoader { - static { - ClassLoader.registerAsParallelCapable(); - } - - /** - * Creates a new jar-in-jar class loader. - * - * @param loaderClassLoader the loader plugin's classloader (setup and created by the platform) - * @param jarResourcePath the path to the jar-in-jar resource within the loader jar - * @throws LoadingException if something unexpectedly bad happens - */ - public JarInJarClassLoader(ClassLoader loaderClassLoader, String jarResourcePath) throws LoadingException { - super(new URL[]{extractJar(loaderClassLoader, jarResourcePath)}, loaderClassLoader); - } - - public void addJarToClasspath(URL url) { - addURL(url); - } - - public void deleteJarResource() { - URL[] urls = getURLs(); - if (urls.length == 0) { - return; - } - - try { - Path path = Paths.get(urls[0].toURI()); - Files.deleteIfExists(path); - } catch (Exception e) { - // ignore - } - } - - /** - * Creates a new plugin instance. - * - * @param bootstrapClass the name of the bootstrap plugin class - * @param loaderPluginType the type of the loader plugin, the only parameter of the bootstrap - * plugin constructor - * @param loaderPlugin the loader plugin instance - * @param the type of the loader plugin - * @return the instantiated bootstrap plugin - */ - public LoaderBootstrap instantiatePlugin(String bootstrapClass, Class loaderPluginType, T loaderPlugin) throws LoadingException { - Class plugin; - try { - plugin = loadClass(bootstrapClass).asSubclass(LoaderBootstrap.class); - } catch (ReflectiveOperationException e) { - throw new LoadingException("Unable to load bootstrap class", e); - } - - Constructor constructor; - try { - constructor = plugin.getConstructor(loaderPluginType); - } catch (ReflectiveOperationException e) { - throw new LoadingException("Unable to get bootstrap constructor", e); - } - - try { - return constructor.newInstance(loaderPlugin); - } catch (ReflectiveOperationException e) { - throw new LoadingException("Unable to create bootstrap plugin instance", e); - } - } - - /** - * Extracts the "jar-in-jar" from the loader plugin into a temporary file, - * then returns a URL that can be used by the {@link JarInJarClassLoader}. - * - * @param loaderClassLoader the classloader for the "host" loader plugin - * @param jarResourcePath the inner jar resource path - * @return a URL to the extracted file - */ - private static URL extractJar(ClassLoader loaderClassLoader, String jarResourcePath) throws LoadingException { - // get the jar-in-jar resource - URL jarInJar = loaderClassLoader.getResource(jarResourcePath); - if (jarInJar == null) { - throw new LoadingException("Could not locate jar-in-jar"); - } - - // create a temporary file - // on posix systems by default this is only read/writable by the process owner - Path path; - try { - path = Files.createTempFile("customfishing-jarinjar", ".jar.tmp"); - } catch (IOException e) { - throw new LoadingException("Unable to create a temporary file", e); - } - - // mark that the file should be deleted on exit - path.toFile().deleteOnExit(); - - // copy the jar-in-jar to the temporary file path - try (InputStream in = jarInJar.openStream()) { - Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - throw new LoadingException("Unable to copy jar-in-jar to temporary path", e); - } - - try { - return path.toUri().toURL(); - } catch (MalformedURLException e) { - throw new LoadingException("Unable to get URL from path", e); - } - } - -} 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 deleted file mode 100644 index 84082f06..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java +++ /dev/null @@ -1,1054 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.action; - -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.manager.ActionManager; -import net.momirealms.customfishing.api.manager.LootManager; -import net.momirealms.customfishing.api.mechanic.GlobalSettings; -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.loot.Loot; -import net.momirealms.customfishing.api.mechanic.requirement.Requirement; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.compatibility.VaultHook; -import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; -import net.momirealms.customfishing.setting.CFLocale; -import net.momirealms.customfishing.util.*; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Entity; -import org.bukkit.entity.ExperienceOrb; -import org.bukkit.entity.Player; -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; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.*; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -public class ActionManagerImpl implements ActionManager { - - private final CustomFishingPlugin plugin; - private final HashMap actionFactoryMap; - private final String EXPANSION_FOLDER = "expansions/action"; - - public ActionManagerImpl(CustomFishingPlugin plugin) { - this.plugin = plugin; - this.actionFactoryMap = new HashMap<>(); - this.registerInbuiltActions(); - } - - // Method to register various built-in actions during initialization. - private void registerInbuiltActions() { - this.registerMessageAction(); - this.registerCommandAction(); - this.registerMendingAction(); - this.registerExpAction(); - this.registerChainAction(); - this.registerPotionAction(); - this.registerSoundAction(); - this.registerPluginExpAction(); - this.registerTitleAction(); - this.registerActionBarAction(); - this.registerCloseInvAction(); - this.registerDelayedAction(); - this.registerConditionalAction(); - this.registerPriorityAction(); - this.registerLevelAction(); - this.registerHologramAction(); - this.registerFakeItemAction(); - this.registerFishFindAction(); - this.registerFoodAction(); - this.registerItemAmountAction(); - this.registerItemDurabilityAction(); - this.registerGiveItemAction(); - this.registerMoneyAction(); - this.registerTimerAction(); - } - - // Method to load expansions and global event actions. - public void load() { - this.loadExpansions(); - this.loadGlobalEventActions(); - } - - public void unload() { - GlobalSettings.unload(); - } - - public void disable() { - unload(); - this.actionFactoryMap.clear(); - } - - // Method to load global event actions from the plugin's configuration file. - private void loadGlobalEventActions() { - YamlConfiguration config = plugin.getConfig("config.yml"); - GlobalSettings.loadEvents(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.actionFactoryMap.containsKey(type)) return false; - this.actionFactoryMap.put(type, actionFactory); - 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.actionFactoryMap.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 - public Action getAction(ConfigurationSection section) { - 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) { - // 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; - } - - /** - * 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) { - // 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) { - Action action = getAction(innerSection); - if (action != null) - actionList.add(action); - } - } - 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 getActionFactory(String type) { - return actionFactoryMap.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; - } - - private void registerMessageAction() { - registerAction("message", (args, chance) -> { - ArrayList msg = ConfigUtils.stringListArgs(args); - return condition -> { - if (Math.random() > chance) return; - List replaced = PlaceholderManagerImpl.getInstance().parse( - condition.getPlayer(), - msg, - condition.getArgs() - ); - for (String text : replaced) { - AdventureHelper.getInstance().sendPlayerMessage(condition.getPlayer(), text); - } - }; - }); - registerAction("broadcast", (args, chance) -> { - ArrayList msg = ConfigUtils.stringListArgs(args); - return condition -> { - if (Math.random() > chance) return; - List replaced = PlaceholderManagerImpl.getInstance().parse( - condition.getPlayer(), - msg, - condition.getArgs() - ); - for (Player player : Bukkit.getOnlinePlayers()) { - for (String text : replaced) { - AdventureHelper.getInstance().sendPlayerMessage(player, text); - } - } - }; - }); - registerAction("message-nearby", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - List msg = section.getStringList("message"); - int range = section.getInt("range"); - return condition -> { - if (Math.random() > chance) return; - Player owner = condition.getPlayer(); - plugin.getScheduler().runTaskSync(() -> { - for (Entity player : condition.getLocation().getWorld().getNearbyEntities(condition.getLocation(), range, range, range, entity -> entity instanceof Player)) { - double distance = LocationUtils.getDistance(player.getLocation(), condition.getLocation()); - if (distance <= range) { - condition.insertArg("{near}", player.getName()); - List replaced = PlaceholderManagerImpl.getInstance().parse( - owner, - msg, - condition.getArgs() - ); - for (String text : replaced) { - AdventureHelper.getInstance().sendPlayerMessage((Player) player, text); - } - condition.delArg("{near}"); - } - } - }, condition.getLocation()); - }; - } else { - LogUtils.warn("Illegal value format found at action: message-nearby"); - return EmptyAction.instance; - } - }); - registerAction("random-message", (args, chance) -> { - ArrayList msg = ConfigUtils.stringListArgs(args); - return condition -> { - if (Math.random() > chance) return; - String random = msg.get(ThreadLocalRandom.current().nextInt(msg.size())); - random = PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), random, condition.getArgs()); - AdventureHelper.getInstance().sendPlayerMessage(condition.getPlayer(), random); - }; - }); - } - - private void registerCommandAction() { - registerAction("command", (args, chance) -> { - ArrayList cmd = ConfigUtils.stringListArgs(args); - return condition -> { - if (Math.random() > chance) return; - List replaced = PlaceholderManagerImpl.getInstance().parse( - condition.getPlayer(), - cmd, - condition.getArgs() - ); - plugin.getScheduler().runTaskSync(() -> { - for (String text : replaced) { - Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text); - } - }, condition.getLocation()); - }; - }); - registerAction("random-command", (args, chance) -> { - ArrayList cmd = ConfigUtils.stringListArgs(args); - return condition -> { - if (Math.random() > chance) return; - String random = cmd.get(ThreadLocalRandom.current().nextInt(cmd.size())); - random = PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), random, condition.getArgs()); - String finalRandom = random; - plugin.getScheduler().runTaskSync(() -> { - Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalRandom); - }, condition.getLocation()); - }; - }); - registerAction("command-nearby", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - List cmd = section.getStringList("command"); - int range = section.getInt("range"); - return condition -> { - if (Math.random() > chance) return; - Player owner = condition.getPlayer(); - plugin.getScheduler().runTaskSync(() -> { - for (Entity player : condition.getLocation().getWorld().getNearbyEntities(condition.getLocation(), range, range, range, entity -> entity instanceof Player)) { - double distance = LocationUtils.getDistance(player.getLocation(), condition.getLocation()); - if (distance <= range) { - condition.insertArg("{near}", player.getName()); - List replaced = PlaceholderManagerImpl.getInstance().parse( - owner, - cmd, - condition.getArgs() - ); - for (String text : replaced) { - Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text); - } - condition.delArg("{near}"); - } - } - }, condition.getLocation()); - }; - } else { - LogUtils.warn("Illegal value format found at action: command-nearby"); - return EmptyAction.instance; - } - }); - } - - private void registerCloseInvAction() { - registerAction("close-inv", (args, chance) -> condition -> { - if (Math.random() > chance) return; - condition.getPlayer().closeInventory(); - }); - } - - private void registerActionBarAction() { - registerAction("actionbar", (args, chance) -> { - String text = (String) args; - return condition -> { - if (Math.random() > chance) return; - String parsed = PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), text, condition.getArgs()); - AdventureHelper.getInstance().sendActionbar(condition.getPlayer(), parsed); - }; - }); - registerAction("random-actionbar", (args, chance) -> { - ArrayList texts = ConfigUtils.stringListArgs(args); - return condition -> { - if (Math.random() > chance) return; - String random = texts.get(ThreadLocalRandom.current().nextInt(texts.size())); - random = PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), random, condition.getArgs()); - AdventureHelper.getInstance().sendActionbar(condition.getPlayer(), random); - }; - }); - registerAction("actionbar-nearby", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - String actionbar = section.getString("actionbar"); - int range = section.getInt("range"); - return condition -> { - if (Math.random() > chance) return; - Player owner = condition.getPlayer(); - plugin.getScheduler().runTaskSync(() -> { - for (Entity player : condition.getLocation().getWorld().getNearbyEntities(condition.getLocation(), range, range, range, entity -> entity instanceof Player)) { - double distance = LocationUtils.getDistance(player.getLocation(), condition.getLocation()); - if (distance <= range) { - condition.insertArg("{near}", player.getName()); - String replaced = PlaceholderManagerImpl.getInstance().parse( - owner, - actionbar, - condition.getArgs() - ); - AdventureHelper.getInstance().sendActionbar((Player) player, replaced); - condition.delArg("{near}"); - } - } - }, condition.getLocation() - ); - }; - } else { - LogUtils.warn("Illegal value format found at action: command-nearby"); - return EmptyAction.instance; - } - }); - } - - private void registerMendingAction() { - registerAction("mending", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return condition -> { - if (Math.random() > chance) return; - if (CustomFishingPlugin.get().getVersionManager().isSpigot()) { - condition.getPlayer().getLocation().getWorld().spawn(condition.getPlayer().getLocation(), ExperienceOrb.class, e -> e.setExperience((int) value.get(condition.getPlayer(), condition.getArgs()))); - } else { - condition.getPlayer().giveExp((int) value.get(condition.getPlayer(), condition.getArgs()), true); - AdventureHelper.getInstance().sendSound(condition.getPlayer(), Sound.Source.PLAYER, Key.key("minecraft:entity.experience_orb.pickup"), 1, 1); - } - }; - }); - } - - private void registerFoodAction() { - registerAction("food", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return condition -> { - if (Math.random() > chance) return; - Player player = condition.getPlayer(); - player.setFoodLevel((int) (player.getFoodLevel() + value.get(player, condition.getArgs()))); - }; - }); - registerAction("saturation", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return condition -> { - if (Math.random() > chance) return; - Player player = condition.getPlayer(); - player.setSaturation((float) (player.getSaturation() + value.get(player, condition.getArgs()))); - }; - }); - } - - private void registerExpAction() { - registerAction("exp", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return condition -> { - if (Math.random() > chance) return; - condition.getPlayer().giveExp((int) value.get(condition.getPlayer(), condition.getArgs())); - AdventureHelper.getInstance().sendSound(condition.getPlayer(), Sound.Source.PLAYER, Key.key("minecraft:entity.experience_orb.pickup"), 1, 1); - }; - }); - } - - private void registerHologramAction() { - registerAction("hologram", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - String text = section.getString("text", ""); - int duration = section.getInt("duration", 20); - boolean position = section.getString("position", "other").equals("other"); - double x = section.getDouble("x"); - double y = section.getDouble("y"); - double z = section.getDouble("z"); - int range = section.getInt("range", 16); - return condition -> { - if (Math.random() > chance) return; - Player owner = condition.getPlayer(); - Location location = position ? condition.getLocation() : owner.getLocation(); - if (range > 0) { - plugin.getScheduler().runTaskSync(() -> { - for (Entity player : location.getWorld().getNearbyEntities(location, range, range, range, entity -> entity instanceof Player)) { - double distance = LocationUtils.getDistance(player.getLocation(), location); - if (distance <= range) { - ArmorStandUtils.sendHologram( - (Player) player, - location.clone().add(x, y, z), - AdventureHelper.getInstance().getComponentFromMiniMessage( - PlaceholderManagerImpl.getInstance().parse(owner, text, condition.getArgs()) - ), - duration - ); - } - } - }, location - ); - } else { - ArmorStandUtils.sendHologram( - owner, - location.clone().add(x, y, z), - AdventureHelper.getInstance().getComponentFromMiniMessage( - PlaceholderManagerImpl.getInstance().parse(owner, text, condition.getArgs()) - ), - duration - ); - } - }; - } else { - LogUtils.warn("Illegal value format found at action: hologram"); - return EmptyAction.instance; - } - }); - } - - private void registerItemAmountAction() { - registerAction("item-amount", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - boolean mainOrOff = section.getString("hand", "main").equalsIgnoreCase("main"); - int amount = section.getInt("amount", 1); - return condition -> { - if (Math.random() > chance) return; - Player player = condition.getPlayer(); - ItemStack itemStack = mainOrOff ? player.getInventory().getItemInMainHand() : player.getInventory().getItemInOffHand(); - itemStack.setAmount(Math.max(0, itemStack.getAmount() + amount)); - }; - } else { - LogUtils.warn("Illegal value format found at action: item-amount"); - return EmptyAction.instance; - } - }); - } - - private void registerItemDurabilityAction() { - registerAction("durability", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - EquipmentSlot slot = EquipmentSlot.valueOf(section.getString("slot", "hand").toUpperCase(Locale.ENGLISH)); - int amount = section.getInt("amount", 1); - return condition -> { - if (Math.random() > chance) return; - Player player = condition.getPlayer(); - ItemStack itemStack = player.getInventory().getItem(slot); - if (amount > 0) { - ItemUtils.increaseDurability(itemStack, amount, true); - } else { - ItemUtils.decreaseDurability(condition.getPlayer(), itemStack, -amount, true); - } - }; - } else { - LogUtils.warn("Illegal value format found at action: durability"); - return EmptyAction.instance; - } - }); - } - - private void registerGiveItemAction() { - registerAction("give-item", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - String id = section.getString("item"); - int amount = section.getInt("amount", 1); - return condition -> { - if (Math.random() > chance) return; - Player player = condition.getPlayer(); - ItemUtils.giveItem(player, Objects.requireNonNull(CustomFishingPlugin.get().getItemManager().buildAnyPluginItemByID(player, id)), amount); - }; - } else { - LogUtils.warn("Illegal value format found at action: give-item"); - return EmptyAction.instance; - } - }); - } - - private void registerFakeItemAction() { - registerAction("fake-item", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - String[] itemSplit = section.getString("item", "").split(":", 2); - int duration = section.getInt("duration", 20); - boolean position = !section.getString("position", "player").equals("player"); - String x = ConfigUtils.getString(section.get("x", "0")); - String y = ConfigUtils.getString(section.get("y", "0")); - String z = ConfigUtils.getString(section.get("z", "0")); - String yaw = ConfigUtils.getString(section.get("yaw", "0")); - int range = section.getInt("range", 0); - boolean opposite = section.getBoolean("opposite-yaw", false); - return condition -> { - if (Math.random() > chance) return; - Player owner = condition.getPlayer(); - Location location = position ? condition.getLocation() : owner.getLocation(); - location = location.clone().add( - plugin.getPlaceholderManager().getExpressionValue(owner, x, condition.getArgs()), - plugin.getPlaceholderManager().getExpressionValue(owner, y, condition.getArgs()), - plugin.getPlaceholderManager().getExpressionValue(owner, z, condition.getArgs()) - ); - Location finalLocation = location; - ItemStack itemStack = plugin.getItemManager().build( - owner, itemSplit[0], - plugin.getPlaceholderManager().parse(owner, itemSplit[1], condition.getArgs()), - condition.getArgs() - ); - if (range > 0) { - plugin.getScheduler().runTaskSync(() -> { - for (Entity player : finalLocation.getWorld().getNearbyEntities(finalLocation, range, range, range, entity -> entity instanceof Player)) { - double distance = LocationUtils.getDistance(player.getLocation(), finalLocation); - if (distance <= range) { - Location locationTemp = finalLocation.clone(); - if (opposite) locationTemp.setYaw(-player.getLocation().getYaw()); - else locationTemp.setYaw((float) plugin.getPlaceholderManager().getExpressionValue((Player) player, yaw, condition.getArgs())); - ArmorStandUtils.sendFakeItem( - condition.getPlayer(), - locationTemp, - itemStack, - duration - ); - } - } - }, condition.getLocation() - ); - } else { - if (opposite) finalLocation.setYaw(-owner.getLocation().getYaw()); - else finalLocation.setYaw((float) plugin.getPlaceholderManager().getExpressionValue(owner, yaw, condition.getArgs())); - ArmorStandUtils.sendFakeItem( - condition.getPlayer(), - finalLocation, - itemStack, - duration - ); - } - }; - } else { - LogUtils.warn("Illegal value format found at action: fake-item"); - return EmptyAction.instance; - } - }); - } - - private void registerChainAction() { - registerAction("chain", (args, chance) -> { - List actions = new ArrayList<>(); - if (args instanceof ConfigurationSection section) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - actions.add(getAction(innerSection)); - } - } - } - return condition -> { - if (Math.random() > chance) return; - for (Action action : actions) { - action.trigger(condition); - } - }; - }); - } - - private void registerMoneyAction() { - registerAction("give-money", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return condition -> { - if (Math.random() > chance) return; - VaultHook.getEconomy().depositPlayer(condition.getPlayer(), value.get(condition.getPlayer(), condition.getArgs())); - }; - }); - registerAction("take-money", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return condition -> { - if (Math.random() > chance) return; - VaultHook.getEconomy().withdrawPlayer(condition.getPlayer(), value.get(condition.getPlayer(), condition.getArgs())); - }; - }); - } - - private void registerDelayedAction() { - registerAction("delay", (args, chance) -> { - List actions = new ArrayList<>(); - int delay; - boolean async; - if (args instanceof ConfigurationSection section) { - delay = section.getInt("delay", 1); - async = section.getBoolean("async", false); - ConfigurationSection actionSection = section.getConfigurationSection("actions"); - if (actionSection != null) { - for (Map.Entry entry : actionSection.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - actions.add(getAction(innerSection)); - } - } - } - } else { - delay = 1; - async = false; - } - return condition -> { - if (Math.random() > chance) return; - if (async) { - plugin.getScheduler().runTaskSyncLater(() -> { - for (Action action : actions) { - action.trigger(condition); - } - }, condition.getLocation(), delay * 50L, TimeUnit.MILLISECONDS); - } else { - plugin.getScheduler().runTaskSyncLater(() -> { - for (Action action : actions) { - action.trigger(condition); - } - }, condition.getLocation(), delay * 50L, TimeUnit.MILLISECONDS); - } - }; - }); - } - - private void registerTimerAction() { - registerAction("timer", (args, chance) -> { - List actions = new ArrayList<>(); - int delay; - int duration; - int period; - boolean async; - if (args instanceof ConfigurationSection section) { - delay = section.getInt("delay", 2); - duration = section.getInt("duration", 20); - period = section.getInt("period", 2); - async = section.getBoolean("async", false); - ConfigurationSection actionSection = section.getConfigurationSection("actions"); - if (actionSection != null) { - for (Map.Entry entry : actionSection.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - actions.add(getAction(innerSection)); - } - } - } - } else { - delay = 1; - async = false; - duration = 20; - period = 1; - } - return condition -> { - if (Math.random() > chance) return; - CancellableTask cancellableTask; - if (async) { - cancellableTask = plugin.getScheduler().runTaskAsyncTimer(() -> { - for (Action action : actions) { - action.trigger(condition); - } - }, delay * 50L, period * 50L, TimeUnit.MILLISECONDS); - } else { - cancellableTask = plugin.getScheduler().runTaskSyncTimer(() -> { - for (Action action : actions) { - action.trigger(condition); - } - }, condition.getLocation(), delay, period); - } - plugin.getScheduler().runTaskSyncLater(cancellableTask::cancel, condition.getLocation(), duration); - }; - }); - } - - private void registerTitleAction() { - registerAction("title", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - String title = section.getString("title"); - String subtitle = section.getString("subtitle"); - int fadeIn = section.getInt("fade-in", 20); - int stay = section.getInt("stay", 30); - int fadeOut = section.getInt("fade-out", 10); - return condition -> { - if (Math.random() > chance) return; - AdventureHelper.getInstance().sendTitle( - condition.getPlayer(), - PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), title, condition.getArgs()), - PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), subtitle, condition.getArgs()), - fadeIn, - stay, - fadeOut - ); - }; - } else { - LogUtils.warn("Illegal value format found at action: title"); - return EmptyAction.instance; - } - }); - registerAction("title-nearby", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - String title = section.getString("title"); - String subtitle = section.getString("subtitle"); - int fadeIn = section.getInt("fade-in", 20); - int stay = section.getInt("stay", 30); - int fadeOut = section.getInt("fade-out", 10); - int range = section.getInt("range", 0); - return condition -> { - if (Math.random() > chance) return; - plugin.getScheduler().runTaskSync(() -> { - for (Entity player : condition.getLocation().getWorld().getNearbyEntities(condition.getLocation(), range, range, range, entity -> entity instanceof Player)) { - double distance = LocationUtils.getDistance(player.getLocation(), condition.getLocation()); - if (distance <= range) { - condition.insertArg("{near}", player.getName()); - AdventureHelper.getInstance().sendTitle( - condition.getPlayer(), - PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), title, condition.getArgs()), - PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), subtitle, condition.getArgs()), - fadeIn, - stay, - fadeOut - ); - condition.delArg("{near}"); - } - } - }, condition.getLocation() - ); - }; - } else { - LogUtils.warn("Illegal value format found at action: title-nearby"); - return EmptyAction.instance; - } - }); - registerAction("random-title", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - List titles = section.getStringList("titles"); - if (titles.size() == 0) titles.add(""); - List subtitles = section.getStringList("subtitles"); - if (subtitles.size() == 0) subtitles.add(""); - int fadeIn = section.getInt("fade-in", 20); - int stay = section.getInt("stay", 30); - int fadeOut = section.getInt("fade-out", 10); - return condition -> { - if (Math.random() > chance) return; - AdventureHelper.getInstance().sendTitle( - condition.getPlayer(), - PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), titles.get(ThreadLocalRandom.current().nextInt(titles.size())), condition.getArgs()), - PlaceholderManagerImpl.getInstance().parse(condition.getPlayer(), subtitles.get(ThreadLocalRandom.current().nextInt(subtitles.size())), condition.getArgs()), - fadeIn, - stay, - fadeOut - ); - }; - } else { - LogUtils.warn("Illegal value format found at action: random-title"); - return EmptyAction.instance; - } - }); - } - - private void registerPotionAction() { - registerAction("potion-effect", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - PotionEffect potionEffect = new PotionEffect( - Objects.requireNonNull(PotionEffectType.getByName(section.getString("type", "BLINDNESS").toUpperCase(Locale.ENGLISH))), - section.getInt("duration", 20), - section.getInt("amplifier", 0) - ); - return condition -> { - if (Math.random() > chance) return; - condition.getPlayer().addPotionEffect(potionEffect); - }; - } else { - LogUtils.warn("Illegal value format found at action: potion-effect"); - return EmptyAction.instance; - } - }); - } - - private void registerLevelAction() { - registerAction("level", (args, chance) -> { - var value = ConfigUtils.getValue(args); - return condition -> { - if (Math.random() > chance) return; - Player player = condition.getPlayer(); - player.setLevel((int) Math.max(0, player.getLevel() + value.get(condition.getPlayer(), condition.getArgs()))); - }; - }); - } - - @SuppressWarnings("all") - private void registerSoundAction() { - registerAction("sound", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - Sound sound = Sound.sound( - Key.key(section.getString("key")), - Sound.Source.valueOf(section.getString("source", "PLAYER").toUpperCase(Locale.ENGLISH)), - (float) section.getDouble("volume", 1), - (float) section.getDouble("pitch", 1) - ); - return condition -> { - if (Math.random() > chance) return; - AdventureHelper.getInstance().sendSound(condition.getPlayer(), sound); - }; - } else { - LogUtils.warn("Illegal value format found at action: sound"); - return EmptyAction.instance; - } - }); - } - - private void registerConditionalAction() { - registerAction("conditional", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - Action[] actions = getActions(section.getConfigurationSection("actions")); - Requirement[] requirements = plugin.getRequirementManager().getRequirements(section.getConfigurationSection("conditions"), true); - return condition -> { - if (Math.random() > chance) return; - if (requirements != null) - for (Requirement requirement : requirements) { - if (!requirement.isConditionMet(condition)) { - return; - } - } - for (Action action : actions) { - action.trigger(condition); - } - }; - } else { - LogUtils.warn("Illegal value format found at action: conditional"); - return EmptyAction.instance; - } - }); - } - - private void registerPriorityAction() { - registerAction("priority", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - List> conditionActionPairList = new ArrayList<>(); - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - Action[] actions = getActions(inner.getConfigurationSection("actions")); - Requirement[] requirements = plugin.getRequirementManager().getRequirements(inner.getConfigurationSection("conditions"), false); - conditionActionPairList.add(Pair.of(requirements, actions)); - } - } - return condition -> { - if (Math.random() > chance) return; - outer: - for (Pair pair : conditionActionPairList) { - if (pair.left() != null) - for (Requirement requirement : pair.left()) { - if (!requirement.isConditionMet(condition)) { - continue outer; - } - } - if (pair.right() != null) - for (Action action : pair.right()) { - action.trigger(condition); - } - return; - } - }; - } else { - LogUtils.warn("Illegal value format found at action: priority"); - return EmptyAction.instance; - } - }); - } - - private void registerPluginExpAction() { - registerAction("plugin-exp", (args, chance) -> { - if (args instanceof ConfigurationSection section) { - String pluginName = section.getString("plugin"); - var value = ConfigUtils.getValue(section.get("exp")); - String target = section.getString("target"); - return condition -> { - if (Math.random() > chance) return; - Optional.ofNullable(plugin.getIntegrationManager().getLevelPlugin(pluginName)).ifPresentOrElse(it -> { - it.addXp(condition.getPlayer(), target, value.get(condition.getPlayer(), condition.getArgs())); - }, () -> LogUtils.warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation.")); - }; - } else { - LogUtils.warn("Illegal value format found at action: plugin-exp"); - return EmptyAction.instance; - } - }); - } - - private void registerFishFindAction() { - registerAction("fish-finder", (args, chance) -> { - boolean arg = (boolean) args; - return condition -> { - if (Math.random() > chance) return; - condition.insertArg("{lava}", String.valueOf(arg)); - LootManager lootManager = plugin.getLootManager(); - 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); - } - condition.delArg("{lava}"); - AdventureHelper.getInstance().sendMessageWithPrefix(condition.getPlayer(), CFLocale.MSG_Possible_Loots + stringJoiner); - }; - }); - } -} 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 deleted file mode 100644 index 27011f2b..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/EmptyAction.java +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index fbec08ec..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.bag; - -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.adventure.AdventureHelper; -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.action.Action; -import net.momirealms.customfishing.api.mechanic.bag.FishingBagHolder; -import net.momirealms.customfishing.api.mechanic.requirement.Requirement; -import net.momirealms.customfishing.api.util.InventoryUtils; -import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; -import net.momirealms.customfishing.setting.CFConfig; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryAction; -import org.bukkit.event.inventory.InventoryClickEvent; -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.*; - -public class BagManagerImpl implements BagManager, Listener { - - private final CustomFishingPlugin plugin; - private final HashMap tempEditMap; - private Action[] collectLootActions; - private Action[] bagFullActions; - private boolean bagStoreLoots; - private String bagTitle; - private List bagWhiteListItems; - private Requirement[] collectRequirements; - - public BagManagerImpl(CustomFishingPluginImpl plugin) { - this.plugin = plugin; - this.tempEditMap = new HashMap<>(); - } - - @Override - public boolean isEnabled() { - return CFConfig.enableFishingBag; - } - - public void load() { - Bukkit.getPluginManager().registerEvents(this, plugin); - YamlConfiguration config = plugin.getConfig("config.yml"); - ConfigurationSection bagSection = config.getConfigurationSection("mechanics.fishing-bag"); - if (bagSection != null) { - bagTitle = bagSection.getString("bag-title"); - bagStoreLoots = bagSection.getBoolean("can-store-loot", false); - bagWhiteListItems = bagSection.getStringList("whitelist-items").stream().map(it -> Material.valueOf(it.toUpperCase(Locale.ENGLISH))).toList(); - if (bagStoreLoots) { - collectLootActions = plugin.getActionManager().getActions(bagSection.getConfigurationSection("collect-actions")); - bagFullActions = plugin.getActionManager().getActions(bagSection.getConfigurationSection("full-actions")); - collectRequirements = plugin.getRequirementManager().getRequirements(bagSection.getConfigurationSection("collect-requirements"), false); - } - } - } - - public void unload() { - HandlerList.unregisterAll(this); - } - - public void disable() { - unload(); - 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); - if (onlinePlayer == null) { - return null; - } - Player player = onlinePlayer.getPlayer(); - int rows = getBagInventoryRows(player); - Inventory bag = onlinePlayer.getHolder().getInventory(); - if (bag.getSize() != rows * 9) { - Inventory newBag = InventoryUtils.createInventory(onlinePlayer.getHolder(), rows * 9, AdventureHelper.getInstance().getComponentFromMiniMessage(PlaceholderManagerImpl.getInstance().parse(player, bagTitle, Map.of("{player}", player.getName())))); - onlinePlayer.getHolder().setInventory(newBag); - assert newBag != null; - ItemStack[] newContents = new ItemStack[rows * 9]; - ItemStack[] oldContents = bag.getContents(); - for (int i = 0; i < rows * 9 && i < oldContents.length; i++) { - newContents[i] = oldContents[i]; - } - newBag.setContents(newContents); - } - return onlinePlayer.getHolder().getInventory(); - } - - @Override - public int getBagInventoryRows(Player player) { - int size = 1; - for (int i = 6; i > 1; i--) { - if (player.hasPermission("fishingbag.rows." + i)) { - size = i; - break; - } - } - return size; - } - - /** - * 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)) - return; - final Player viewer = (Player) event.getPlayer(); - OfflineUser offlineUser = tempEditMap.remove(viewer.getUniqueId()); - if (offlineUser == null) - return; - 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()) - return; - if (!(event.getInventory().getHolder() instanceof FishingBagHolder)) - return; - Inventory clicked = event.getClickedInventory(); - ItemStack clickedItem = event.getCurrentItem(); - if (clicked != event.getWhoClicked().getInventory()) { - if (event.getAction() != InventoryAction.HOTBAR_SWAP && event.getAction() != InventoryAction.HOTBAR_MOVE_AND_READD) { - return; - } - clickedItem = event.getWhoClicked().getInventory().getItem(event.getHotbarButton()); - } - if (clickedItem == null || clickedItem.getType() == Material.AIR) - return; - if (bagWhiteListItems.contains(clickedItem.getType())) - return; - 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 (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()); - if (offlineUser == null) - return; - plugin.getStorageManager().saveUserData(offlineUser, true); - } - - @Override - public Action[] getCollectLootActions() { - return collectLootActions; - } - - @Override - public Action[] getBagFullActions() { - return bagFullActions; - } - - @Override - public boolean doesBagStoreLoots() { - return bagStoreLoots; - } - - @Override - public String getBagTitle() { - return bagTitle; - } - - @Override - public List getBagWhiteListItems() { - return bagWhiteListItems; - } - - @Override - public Requirement[] getCollectRequirements() { - return collectRequirements; - } -} 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 deleted file mode 100644 index 66514fc0..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java +++ /dev/null @@ -1,529 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.block; - -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.common.Tuple; -import net.momirealms.customfishing.api.manager.BlockManager; -import net.momirealms.customfishing.api.mechanic.block.*; -import net.momirealms.customfishing.api.mechanic.loot.Loot; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.compatibility.block.VanillaBlockImpl; -import net.momirealms.customfishing.setting.CFConfig; -import net.momirealms.customfishing.util.ConfigUtils; -import org.bukkit.*; -import org.bukkit.block.*; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Directional; -import org.bukkit.block.data.Rotatable; -import org.bukkit.block.data.type.Campfire; -import org.bukkit.block.data.type.Farmland; -import org.bukkit.block.data.type.NoteBlock; -import org.bukkit.block.data.type.TurtleEgg; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Ageable; -import org.bukkit.entity.FallingBlock; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.EntityChangeBlockEvent; -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.*; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -public class BlockManagerImpl implements BlockManager, Listener { - - private final CustomFishingPlugin plugin; - private final HashMap blockLibraryMap; - private BlockLibrary[] blockDetectionArray; - private final HashMap blockConfigMap; - private final HashMap dataBuilderMap; - private final HashMap stateBuilderMap; - - public BlockManagerImpl(CustomFishingPluginImpl plugin) { - this.plugin = plugin; - this.blockLibraryMap = new HashMap<>(); - this.blockConfigMap = new HashMap<>(); - this.dataBuilderMap = new HashMap<>(); - this.stateBuilderMap = new HashMap<>(); - this.registerInbuiltProperties(); - this.registerBlockLibrary(new VanillaBlockImpl()); - } - - public void load() { - this.loadConfig(); - Bukkit.getPluginManager().registerEvents(this, plugin); - this.resetBlockDetectionOrder(); - } - - public void unload() { - HandlerList.unregisterAll(this); - HashMap tempMap = new HashMap<>(this.blockConfigMap); - this.blockConfigMap.clear(); - for (Map.Entry entry : tempMap.entrySet()) { - if (entry.getValue().isPersist()) { - tempMap.put(entry.getKey(), entry.getValue()); - } - } - } - - public void disable() { - this.blockLibraryMap.clear(); - } - - private void resetBlockDetectionOrder() { - ArrayList list = new ArrayList<>(); - for (String plugin : CFConfig.blockDetectOrder) { - BlockLibrary library = blockLibraryMap.get(plugin); - if (library != null) { - list.add(library); - } - } - this.blockDetectionArray = list.toArray(new BlockLibrary[0]); - } - - /** - * 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; - - // 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(); - event.getBlock().setType(Material.AIR); - 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()) { - modifier.apply(player, state); - } - }, 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 blockLibrary) { - if (this.blockLibraryMap.containsKey(blockLibrary.identification())) return false; - this.blockLibraryMap.put(blockLibrary.identification(), blockLibrary); - this.resetBlockDetectionOrder(); - 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(String identification) { - boolean success = blockLibraryMap.remove(identification) != null; - if (success) - this.resetBlockDetectionOrder(); - return success; - } - - /** - * 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; - dataBuilderMap.put(type, builder); - 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; - stateBuilderMap.put(type, builder); - 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; - } - - private void registerInbuiltProperties() { - this.registerDirectional(); - this.registerStorage(); - this.registerRotatable(); - this.registerTurtleEggs(); - this.registerMoisture(); - this.registerNoteBlock(); - this.registerCampfire(); - this.registerAge(); - } - - /** - * 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<>(); - for (String type : List.of("block")) { - File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type); - if (!typeFolder.exists()) { - if (!typeFolder.mkdirs()) return; - plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false); - } - fileDeque.push(typeFolder); - while (!fileDeque.isEmpty()) { - File file = fileDeque.pop(); - File[] files = file.listFiles(); - if (files == null) continue; - for (File subFile : files) { - if (subFile.isDirectory()) { - fileDeque.push(subFile); - } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { - this.loadSingleFile(subFile); - } - } - } - } - } - - /** - * 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()); - continue; - } - 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()) { - BlockDataModifierBuilder dataBuilder = dataBuilderMap.get(innerEntry.getKey()); - if (dataBuilder != null) { - dataModifiers.add(dataBuilder.build(innerEntry.getValue())); - continue; - } - BlockStateModifierBuilder stateBuilder = stateBuilderMap.get(innerEntry.getKey()); - if (stateBuilder != null) { - stateModifiers.add(stateBuilder.build(innerEntry.getValue())); - } - } - } - - // Create a BlockConfig instance with the processed data and add it to the blockConfigMap. - BlockConfig blockConfig = new BlockConfig.Builder() - .blockID(blockID) - .persist(false) - .horizontalVector(section.getDouble("velocity.horizontal", 1.1)) - .verticalVector(section.getDouble("velocity.vertical", 1.2)) - .dataModifiers(dataModifiers) - .stateModifiers(stateModifiers) - .build(); - blockConfigMap.put(entry.getKey(), blockConfig); - } - } - } - - /** - * 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()); - if (config == null) { - LogUtils.warn("Block: " + loot.getID() + " doesn't exist."); - return; - } - String blockID = config.getBlockID(); - BlockData blockData; - if (blockID.contains(":")) { - String[] split = blockID.split(":", 2); - String lib = split[0]; - String id = split[1]; - blockData = blockLibraryMap.get(lib).getBlockData(player, id, config.getDataModifier()); - } else { - blockData = blockLibraryMap.get("vanilla").getBlockData(player, blockID, config.getDataModifier()); - } - FallingBlock fallingBlock = hookLocation.getWorld().spawnFallingBlock(hookLocation, blockData); - fallingBlock.getPersistentDataContainer().set( - Objects.requireNonNull(NamespacedKey.fromString("block", CustomFishingPlugin.get())), - PersistentDataType.STRING, - loot.getID() + ";" + player.getName() - ); - Vector vector = playerLocation.subtract(hookLocation).toVector().multiply((config.getHorizontalVector()) - 1); - vector = vector.setY((vector.getY() + 0.2) * config.getVerticalVector()); - 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 - @NotNull - public String getAnyPluginBlockID(Block block) { - for (BlockLibrary blockLibrary : blockDetectionArray) { - String id = blockLibrary.getBlockID(block); - if (id != null) { - return id; - } - } - // Should not reach this because vanilla library would always work - return "AIR"; - } - - private void registerDirectional() { - this.registerBlockDataModifierBuilder("directional-4", (args) -> (player, blockData) -> { - boolean arg = (boolean) args; - if (arg && blockData instanceof Directional directional) { - directional.setFacing(BlockFace.values()[ThreadLocalRandom.current().nextInt(0, 4)]); - } - }); - this.registerBlockDataModifierBuilder("directional-6", (args) -> (player, blockData) -> { - boolean arg = (boolean) args; - if (arg && blockData instanceof Directional directional) { - directional.setFacing(BlockFace.values()[ThreadLocalRandom.current().nextInt(0, 6)]); - } - }); - } - - private void registerMoisture() { - this.registerBlockDataModifierBuilder("moisture", (args) -> { - int arg = (int) args; - return (player, blockData) -> { - if (blockData instanceof Farmland farmland) { - farmland.setMoisture(arg); - } - }; - }); - } - - private void registerCampfire() { - this.registerBlockDataModifierBuilder("campfire", (args) -> { - boolean arg = (boolean) args; - return (player, blockData) -> { - if (blockData instanceof Campfire campfire) { - campfire.setSignalFire(arg); - } - }; - }); - } - - private void registerRotatable() { - this.registerBlockDataModifierBuilder("rotatable", (args) -> { - boolean arg = (boolean) args; - return (player, blockData) -> { - if (arg && blockData instanceof Rotatable rotatable) { - rotatable.setRotation(BlockFace.values()[ThreadLocalRandom.current().nextInt(BlockFace.values().length)]); - } - }; - }); - } - - private void registerNoteBlock() { - this.registerBlockDataModifierBuilder("noteblock", (args) -> { - if (args instanceof ConfigurationSection section) { - var instrument = Instrument.valueOf(section.getString("instrument")); - var note = new Note(section.getInt("note")); - return (player, blockData) -> { - if (blockData instanceof NoteBlock noteBlock) { - noteBlock.setNote(note); - noteBlock.setInstrument(instrument); - } - }; - } else { - LogUtils.warn("Invalid property format found at block noteblock."); - return null; - } - }); - } - - private void registerAge() { - this.registerBlockDataModifierBuilder("age", (args) -> { - int arg = (int) args; - return (player, blockData) -> { - if (blockData instanceof Ageable ageable) { - ageable.setAge(arg); - } - }; - }); - } - - private void registerTurtleEggs() { - this.registerBlockDataModifierBuilder("turtle-eggs", (args) -> { - int arg = (int) args; - return (player, blockData) -> { - if (blockData instanceof TurtleEgg egg) { - egg.setEggs(arg); - } - }; - }); - } - - private void registerStorage() { - this.registerBlockStateModifierBuilder("storage", (args) -> { - if (args instanceof ConfigurationSection section) { - ArrayList>> tempChanceList = new ArrayList<>(); - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - String item = inner.getString("item"); - Pair amountPair = ConfigUtils.splitStringIntegerArgs(inner.getString("amount","1~1"), "~"); - double chance = inner.getDouble("chance", 1); - tempChanceList.add(Tuple.of(chance, item, amountPair)); - } - } - return (player, blockState) -> { - if (blockState instanceof Chest chest) { - setInventoryItems(tempChanceList, player, chest.getInventory()); - return; - } - if (blockState instanceof Barrel barrel) { - setInventoryItems(tempChanceList, player, barrel.getInventory()); - return; - } - if (blockState instanceof ShulkerBox shulkerBox) { - setInventoryItems(tempChanceList, player, shulkerBox.getInventory()); - return; - } - }; - } else { - LogUtils.warn("Invalid property format found at block storage."); - return null; - } - }); - } - - /** - * 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, - Inventory inventory - ) { - LinkedList unused = new LinkedList<>(); - for (int i = 0; i < 27; i++) { - unused.add(i); - } - Collections.shuffle(unused); - for (Tuple> tuple : tempChanceList) { - 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 deleted file mode 100644 index 17ac9644..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/Competition.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.competition; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.event.CompetitionEvent; -import net.momirealms.customfishing.api.mechanic.action.Action; -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 net.momirealms.customfishing.api.mechanic.competition.Ranking; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import net.momirealms.customfishing.mechanic.competition.actionbar.ActionBarManager; -import net.momirealms.customfishing.mechanic.competition.bossbar.BossBarManager; -import net.momirealms.customfishing.mechanic.competition.ranking.LocalRankingImpl; -import net.momirealms.customfishing.mechanic.competition.ranking.RedisRankingImpl; -import net.momirealms.customfishing.setting.CFConfig; -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; - -public class Competition implements FishingCompetition { - - private final CompetitionConfig config; - private CancellableTask competitionTimerTask; - private final CompetitionGoal goal; - private final ConcurrentHashMap publicPlaceholders; - private final Ranking ranking; - private float progress; - private long remainingTime; - private long startTime; - private BossBarManager bossBarManager; - private ActionBarManager actionBarManager; - - public Competition(CompetitionConfig config) { - this.config = config; - this.goal = config.getGoal() == CompetitionGoal.RANDOM ? CompetitionGoal.getRandom() : config.getGoal(); - if (CFConfig.redisRanking) this.ranking = new RedisRankingImpl(); - else this.ranking = new LocalRankingImpl(); - this.publicPlaceholders = new ConcurrentHashMap<>(); - 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.getDurationInSeconds(); - this.startTime = Instant.now().getEpochSecond(); - - this.arrangeTimerTask(); - if (config.getBossBarConfig() != null) { - this.bossBarManager = new BossBarManager(config.getBossBarConfig(), this); - this.bossBarManager.load(); - } - if (config.getActionBarConfig() != null) { - this.actionBarManager = new ActionBarManager(config.getActionBarConfig(), this); - this.actionBarManager.load(); - } - - Action[] actions = config.getStartActions(); - if (actions != null) { - Condition condition = new Condition(null, null, this.publicPlaceholders); - for (Action action : actions) { - action.trigger(condition); - } - } - - this.ranking.clear(); - this.updatePublicPlaceholders(); - - CompetitionEvent competitionStartEvent = new CompetitionEvent(CompetitionEvent.State.START, this); - Bukkit.getPluginManager().callEvent(competitionStartEvent); - } - - /** - * 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()) { - end(true); - return; - } - updatePublicPlaceholders(); - }, 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; - Optional.ofNullable(ranking.getPlayerAt(i)).ifPresentOrElse(player -> { - publicPlaceholders.put("{" + finalI + "_player}", player); - publicPlaceholders.put("{" + finalI + "_score}", String.format("%.2f", ranking.getScoreAt(finalI))); - }, () -> { - publicPlaceholders.put("{" + finalI + "_player}", CFLocale.MSG_No_Player); - publicPlaceholders.put("{" + finalI + "_score}", CFLocale.MSG_No_Score); - }); - } - publicPlaceholders.put("{hour}", remainingTime < 3600 ? "" : (remainingTime / 3600) + CFLocale.FORMAT_Hour); - publicPlaceholders.put("{minute}", remainingTime < 60 ? "" : (remainingTime % 3600) / 60 + CFLocale.FORMAT_Minute); - publicPlaceholders.put("{second}", remainingTime == 0 ? "" : remainingTime % 60 + CFLocale.FORMAT_Second); - 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(boolean triggerEvent) { - if (!competitionTimerTask.isCancelled()) this.competitionTimerTask.cancel(); - if (this.bossBarManager != null) this.bossBarManager.unload(); - if (this.actionBarManager != null) this.actionBarManager.unload(); - this.ranking.clear(); - this.remainingTime = 0; - - if (triggerEvent) { - CompetitionEvent competitionEvent = new CompetitionEvent(CompetitionEvent.State.STOP, this); - Bukkit.getPluginManager().callEvent(competitionEvent); - } - } - - /** - * 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(boolean triggerEvent) { - // mark it as ended - this.remainingTime = 0; - - // cancel some sub tasks - if (!competitionTimerTask.isCancelled()) this.competitionTimerTask.cancel(); - if (this.bossBarManager != null) this.bossBarManager.unload(); - if (this.actionBarManager != null) this.actionBarManager.unload(); - - // give prizes - HashMap rewardsMap = config.getRewards(); - if (ranking.getSize() != 0 && rewardsMap != null) { - Iterator> iterator = ranking.getIterator(); - int i = 1; - while (iterator.hasNext()) { - Pair competitionPlayer = iterator.next(); - this.publicPlaceholders.put("{" + i + "_player}", competitionPlayer.left()); - this.publicPlaceholders.put("{" + i + "_score}", String.format("%.2f", competitionPlayer.right())); - if (i < rewardsMap.size()) { - Player player = Bukkit.getPlayer(competitionPlayer.left()); - if (player != null) - for (Action action : rewardsMap.get(String.valueOf(i))) - action.trigger(new Condition(player, this.publicPlaceholders)); - } else { - Action[] actions = rewardsMap.get("participation"); - if (actions != null) { - Player player = Bukkit.getPlayer(competitionPlayer.left()); { - if (player != null) - for (Action action : actions) - action.trigger(new Condition(player, this.publicPlaceholders)); - } - } - } - i++; - } - } - - // do end actions - Action[] actions = config.getEndActions(); - if (actions != null) { - Condition condition = new Condition(null, null, new HashMap<>(publicPlaceholders)); - for (Action action : actions) { - action.trigger(condition); - } - } - - // call event - if (triggerEvent) { - CompetitionEvent competitionEndEvent = new CompetitionEvent(CompetitionEvent.State.END, this); - Bukkit.getPluginManager().callEvent(competitionEndEvent); - } - - // 1 seconds delay for other servers to read the redis data - CustomFishingPlugin.get().getScheduler().runTaskAsyncLater(this.ranking::clear, 1, TimeUnit.SECONDS); - } - - /** - * 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.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 - if (!hasPlayerJoined(player)) { - Action[] actions = config.getJoinActions(); - if (actions != null) { - Condition condition = new Condition(player); - for (Action action : actions) { - action.trigger(condition); - } - } - } - - // show competition info - if (this.bossBarManager != null) this.bossBarManager.showBossBarTo(player); - if (this.actionBarManager != null) this.actionBarManager.showActionBarTo(player); - - // refresh data - switch (this.goal) { - case CATCH_AMOUNT -> ranking.refreshData(player.getName(), 1); - case TOTAL_SIZE, TOTAL_SCORE -> ranking.refreshData(player.getName(), score); - case MAX_SIZE -> { - if (score > ranking.getPlayerScore(player.getName())) { - ranking.setData(player.getName(), 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. - */ - @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 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/actionbar/ActionBarSender.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/actionbar/ActionBarSender.java deleted file mode 100644 index 84124c45..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/actionbar/ActionBarSender.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.competition.actionbar; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.mechanic.competition.ActionBarConfig; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import net.momirealms.customfishing.mechanic.competition.Competition; -import net.momirealms.customfishing.mechanic.misc.DynamicText; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.entity.Player; - -import java.util.HashMap; -import java.util.concurrent.TimeUnit; - -/** - * Manages and updates ActionBar messages for a specific player in a competition context. - */ -public class ActionBarSender { - - private final Player player; - private int refreshTimer; - private int switchTimer; - private int counter; - private final DynamicText[] texts; - private CancellableTask senderTask; - private final ActionBarConfig config; - private boolean isShown; - private final Competition competition; - private final HashMap privatePlaceholders; - - /** - * Creates a new ActionBarSender instance for a player. - * - * @param player The player to manage ActionBar messages for. - * @param config The configuration for ActionBar messages. - * @param competition The competition associated with this ActionBarSender. - */ - public ActionBarSender(Player player, ActionBarConfig config, Competition competition) { - this.player = player; - this.config = config; - this.isShown = false; - this.competition = competition; - this.privatePlaceholders = new HashMap<>(); - this.privatePlaceholders.put("{player}", player.getName()); - this.updatePrivatePlaceholders(); - - String[] str = config.getTexts(); - texts = new DynamicText[str.length]; - for (int i = 0; i < str.length; i++) { - texts[i] = new DynamicText(player, str[i]); - texts[i].update(privatePlaceholders); - } - } - - /** - * Updates private placeholders used in ActionBar messages. - */ - @SuppressWarnings("DuplicatedCode") - private void updatePrivatePlaceholders() { - this.privatePlaceholders.put("{score}", String.format("%.2f", competition.getRanking().getPlayerScore(player.getName()))); - int rank = competition.getRanking().getPlayerRank(player.getName()); - this.privatePlaceholders.put("{rank}", rank != -1 ? String.valueOf(rank) : CFLocale.MSG_No_Rank); - this.privatePlaceholders.putAll(competition.getCachedPlaceholders()); - } - - /** - * Shows the ActionBar message to the player. - */ - public void show() { - this.isShown = true; - senderTask = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(() -> { - switchTimer++; - if (switchTimer > config.getSwitchInterval()) { - switchTimer = 0; - counter++; - } - if (refreshTimer < config.getRefreshRate()){ - refreshTimer++; - } else { - refreshTimer = 0; - DynamicText text = texts[counter % (texts.length)]; - updatePrivatePlaceholders(); - text.update(privatePlaceholders); - AdventureHelper.getInstance().sendActionbar( - player, - text.getLatestValue() - ); - } - }, 50, 50, TimeUnit.MILLISECONDS); - } - - /** - * Hides the ActionBar message from the player. - */ - public void hide() { - if (senderTask != null && !senderTask.isCancelled()) - senderTask.cancel(); - this.isShown = false; - } - - /** - * Checks if the ActionBar message is currently visible to the player. - * - * @return True if the ActionBar message is visible, false otherwise. - */ - public boolean isVisible() { - return this.isShown; - } - - /** - * Gets the ActionBar configuration. - * - * @return The ActionBar configuration. - */ - public ActionBarConfig getConfig() { - return config; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/bossbar/BossBarSender.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/bossbar/BossBarSender.java deleted file mode 100644 index 262ceda3..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/bossbar/BossBarSender.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.competition.bossbar; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.InternalStructure; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.wrappers.WrappedChatComponent; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.mechanic.competition.BossBarConfig; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import net.momirealms.customfishing.api.util.ReflectionUtils; -import net.momirealms.customfishing.mechanic.competition.Competition; -import net.momirealms.customfishing.mechanic.misc.DynamicText; -import net.momirealms.customfishing.setting.CFLocale; -import org.bukkit.boss.BarColor; -import org.bukkit.entity.Player; - -import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -/** - * Manages and updates boss bars for a specific player in a competition context. - */ -public class BossBarSender { - - private final Player player; - private int refreshTimer; - private int switchTimer; - private int counter; - private final DynamicText[] texts; - private CancellableTask senderTask; - private final UUID uuid; - private final BossBarConfig config; - private boolean isShown; - private final Competition competition; - private final HashMap privatePlaceholders; - - /** - * Creates a new BossBarSender instance for a player. - * - * @param player The player to manage the boss bar for. - * @param config The configuration for the boss bar. - * @param competition The competition associated with this boss bar. - */ - public BossBarSender(Player player, BossBarConfig config, Competition competition) { - this.player = player; - this.uuid = UUID.randomUUID(); - this.config = config; - this.isShown = false; - this.competition = competition; - this.privatePlaceholders = new HashMap<>(); - this.privatePlaceholders.put("{player}", player.getName()); - this.updatePrivatePlaceholders(); - - String[] str = config.getTexts(); - texts = new DynamicText[str.length]; - for (int i = 0; i < str.length; i++) { - texts[i] = new DynamicText(player, str[i]); - texts[i].update(privatePlaceholders); - } - } - - /** - * Updates private placeholders used in boss bar messages. - */ - @SuppressWarnings("DuplicatedCode") - private void updatePrivatePlaceholders() { - this.privatePlaceholders.put("{score}", String.format("%.2f", competition.getRanking().getPlayerScore(player.getName()))); - int rank = competition.getRanking().getPlayerRank(player.getName()); - this.privatePlaceholders.put("{rank}", rank != -1 ? String.valueOf(rank) : CFLocale.MSG_No_Rank); - this.privatePlaceholders.putAll(competition.getCachedPlaceholders()); - } - - /** - * Shows the boss bar to the player. - */ - public void show() { - this.isShown = true; - CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getCreatePacket()); - senderTask = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(() -> { - switchTimer++; - if (switchTimer > config.getSwitchInterval()) { - switchTimer = 0; - counter++; - } - if (refreshTimer < config.getRefreshRate()){ - refreshTimer++; - } else { - refreshTimer = 0; - DynamicText text = texts[counter % (texts.length)]; - updatePrivatePlaceholders(); - if (text.update(privatePlaceholders)) { - CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getUpdatePacket(text)); - } - CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getProgressPacket()); - } - }, 50, 50, TimeUnit.MILLISECONDS); - } - - /** - * Checks if the boss bar is currently visible to the player. - * - * @return True if the boss bar is visible, false otherwise. - */ - public boolean isVisible() { - return this.isShown; - } - - /** - * Gets the boss bar configuration. - * - * @return The boss bar configuration. - */ - public BossBarConfig getConfig() { - return config; - } - - /** - * Hides the boss bar from the player. - */ - public void hide() { - CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getRemovePacket()); - if (senderTask != null && !senderTask.isCancelled()) senderTask.cancel(); - this.isShown = false; - } - - private PacketContainer getUpdatePacket(DynamicText text) { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.BOSS); - packet.getModifier().write(0, uuid); - try { - Object chatComponent = ReflectionUtils.iChatComponentMethod.invoke(null, - GsonComponentSerializer.gson().serialize( - AdventureHelper.getInstance().getComponentFromMiniMessage( - text.getLatestValue() - ))); - Object updatePacket = ReflectionUtils.updateConstructor.newInstance(chatComponent); - packet.getModifier().write(1, updatePacket); - } catch (InvocationTargetException | IllegalAccessException | InstantiationException e) { - throw new RuntimeException(e); - } - return packet; - } - - private PacketContainer getProgressPacket() { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.BOSS); - packet.getModifier().write(0, uuid); - try { - Object updatePacket = ReflectionUtils.progressConstructor.newInstance(competition.getProgress()); - packet.getModifier().write(1, updatePacket); - } catch (InvocationTargetException | IllegalAccessException | InstantiationException e) { - throw new RuntimeException(e); - } - return packet; - } - - private PacketContainer getCreatePacket() { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.BOSS); - packet.getModifier().write(0, uuid); - InternalStructure internalStructure = packet.getStructures().read(1); - internalStructure.getChatComponents().write(0, WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(texts[0].getLatestValue())))); - internalStructure.getFloat().write(0, competition.getProgress()); - internalStructure.getEnumModifier(BarColor.class, 2).write(0, config.getColor()); - internalStructure.getEnumModifier(BossBarConfig.Overlay.class, 3).write(0, config.getOverlay()); - internalStructure.getModifier().write(4, false); - internalStructure.getModifier().write(5, false); - internalStructure.getModifier().write(6, false); - return packet; - } - - private PacketContainer getRemovePacket() { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.BOSS); - packet.getModifier().write(0, uuid); - packet.getModifier().write(1, ReflectionUtils.removeBossBarPacket); - return packet; - } -} 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 deleted file mode 100644 index fc0f978b..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/effect/EffectManagerImpl.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.effect; - -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.GlobalSettings; -import net.momirealms.customfishing.api.mechanic.effect.BaseEffect; -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 net.momirealms.customfishing.api.mechanic.misc.Value; -import net.momirealms.customfishing.api.mechanic.misc.WeightModifier; -import net.momirealms.customfishing.api.mechanic.requirement.Requirement; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.mechanic.misc.value.PlainValue; -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; -import java.util.*; - -public class EffectManagerImpl implements EffectManager { - - private final CustomFishingPlugin plugin; - - private final HashMap effectMap; - - public EffectManagerImpl(CustomFishingPlugin plugin) { - this.plugin = plugin; - this.effectMap = new HashMap<>(); - } - - public void disable() { - 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 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 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 getEffectCarrier(String namespace, String id) { - return effectMap.get(Key.of(namespace, id)); - } - - public void load() { - this.loadFiles(); - this.loadGlobalEffects(); - } - - /** - * 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") - private void loadFiles() { - Deque fileDeque = new ArrayDeque<>(); - for (String type : List.of("rod", "bait", "enchant", "util", "totem", "hook")) { - File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type); - if (!typeFolder.exists()) { - if (!typeFolder.mkdirs()) return; - plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false); - } - fileDeque.push(typeFolder); - while (!fileDeque.isEmpty()) { - File file = fileDeque.pop(); - File[] files = file.listFiles(); - if (files == null) continue; - for (File subFile : files) { - if (subFile.isDirectory()) { - fileDeque.push(subFile); - } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { - this.loadSingleFile(subFile, type); - } - } - } - } - } - - /** - * 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 = getEffectCarrierFromSection(key, section); - if (item != null) - effectMap.put(key, item); - } - } - } - - /** - * 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) - .requirements(plugin.getRequirementManager().getRequirements(section.getConfigurationSection("requirements"), true)) - .effect(getEffectModifiers(section.getConfigurationSection("effects"))) - .actionMap(plugin.getActionManager().getActionMap(section.getConfigurationSection("events"))) - .build(); - } - - public void unload() { - HashMap temp = new HashMap<>(effectMap); - effectMap.clear(); - for (Map.Entry entry : temp.entrySet()) { - if (entry.getValue().isPersist()) { - effectMap.put(entry.getKey(), entry.getValue()); - } - } - } - - /** - * Retrieves the initial FishingEffect that represents no special effects. - * - * @return The initial FishingEffect. - */ - @NotNull - @Override - public FishingEffect getInitialEffect() { - return new FishingEffect(); - } - - /** - * 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) { - String[] split = group.split(":",2); - String key = split[0]; - List members = plugin.getLootManager().getLootGroup(key); - if (members == null) { - LogUtils.warn("Group " + key + " doesn't contain any loot. The effect would not take effect."); - return result; - } - for (String loot : members) { - result.add(Pair.of(loot, ConfigUtils.getModifier(split[1]))); - } - } - 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<>(); - for (Map.Entry entry: section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - EffectModifier effectModifier = getEffectModifier(inner); - if (effectModifier != null) - modifiers.add(effectModifier); - } - } - return modifiers.toArray(new EffectModifier[0]); - } - - @Override - public BaseEffect getBaseEffect(ConfigurationSection section) { - if (section == null) return new BaseEffect( - new PlainValue(0), new PlainValue(1d), - new PlainValue(0), new PlainValue(1d), - new PlainValue(0), new PlainValue(1d) - ); - Value waitTime = section.contains("wait-time") ? ConfigUtils.getValue(section.get("wait-time")) : new PlainValue(0); - Value difficulty = section.contains("difficulty") ? ConfigUtils.getValue(section.get("difficulty")) : new PlainValue(0); - Value gameTime = section.contains("game-time") ? ConfigUtils.getValue(section.get("game-time")) : new PlainValue(0); - Value waitTimeMultiplier = section.contains("wait-time-multiplier") ? ConfigUtils.getValue(section.get("wait-time-multiplier")) : new PlainValue(1); - Value difficultyMultiplier = section.contains("difficulty-multiplier") ? ConfigUtils.getValue(section.get("difficulty-multiplier")) : new PlainValue(1); - Value gameTimeMultiplier = section.contains("game-time-multiplier") ? ConfigUtils.getValue(section.get("game-time-multiplier")) : new PlainValue(1); - return new BaseEffect( - waitTime, waitTimeMultiplier, - difficulty, difficultyMultiplier, - gameTime, gameTimeMultiplier - ); - } - - private void loadGlobalEffects() { - YamlConfiguration config = plugin.getConfig("config.yml"); - ConfigurationSection section = config.getConfigurationSection("mechanics.global-effects"); - GlobalSettings.setEffects(getEffectModifiers(section)); - } - - /** - * 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; - switch (type) { - case "weight-mod" -> { - var modList = ConfigUtils.getModifiers(section.getStringList("value")); - return ((effect, condition) -> { - effect.addWeightModifier(modList); - }); - } - case "weight-mod-ignore-conditions" -> { - var modList = ConfigUtils.getModifiers(section.getStringList("value")); - return ((effect, condition) -> { - effect.addWeightModifierIgnored(modList); - }); - } - case "group-mod" -> { - var modList = getGroupModifiers(section.getStringList("value")); - return ((effect, condition) -> { - effect.addWeightModifier(modList); - }); - } - case "group-mod-ignore-conditions" -> { - var modList = getGroupModifiers(section.getStringList("value")); - return ((effect, condition) -> { - effect.addWeightModifierIgnored(modList); - }); - } - case "wait-time" -> { - var value = ConfigUtils.getValue(section.get("value")); - return ((effect, condition) -> { - effect.setWaitTime(effect.getWaitTime() + value.get(condition.getPlayer(), condition.getArgs())); - }); - } - case "hook-time", "wait-time-multiplier" -> { - var value = ConfigUtils.getValue(section.get("value")); - return ((effect, condition) -> { - effect.setWaitTimeMultiplier(effect.getWaitTimeMultiplier() + value.get(condition.getPlayer(), condition.getArgs()) - 1); - }); - } - case "difficulty" -> { - var value = ConfigUtils.getValue(section.get("value")); - return ((effect, condition) -> { - effect.setDifficulty(effect.getDifficulty() + value.get(condition.getPlayer(), condition.getArgs())); - }); - } - case "difficulty-multiplier", "difficulty-bonus" -> { - var value = ConfigUtils.getValue(section.get("value")); - return ((effect, condition) -> { - effect.setDifficultyMultiplier(effect.getDifficultyMultiplier() + value.get(condition.getPlayer(), condition.getArgs()) - 1); - }); - } - case "multiple-loot" -> { - var value = ConfigUtils.getValue(section.get("value")); - return ((effect, condition) -> { - effect.setMultipleLootChance(effect.getMultipleLootChance() + value.get(condition.getPlayer(), condition.getArgs())); - }); - } - case "score" -> { - var value = ConfigUtils.getValue(section.get("value")); - return ((effect, condition) -> { - effect.setScore(effect.getScore() + value.get(condition.getPlayer(), condition.getArgs())); - }); - } - case "score-bonus", "score-multiplier" -> { - var value = ConfigUtils.getValue(section.get("value")); - return ((effect, condition) -> { - effect.setScoreMultiplier(effect.getScoreMultiplier() + value.get(condition.getPlayer(), condition.getArgs()) - 1); - }); - } - case "size" -> { - var value = ConfigUtils.getValue(section.get("value")); - return ((effect, condition) -> { - effect.setSize(effect.getSize() + value.get(condition.getPlayer(), condition.getArgs())); - }); - } - case "size-bonus", "size-multiplier" -> { - var value = ConfigUtils.getValue(section.get("value")); - return ((effect, condition) -> { - effect.setSizeMultiplier(effect.getSizeMultiplier() + value.get(condition.getPlayer(), condition.getArgs()) - 1); - }); - } - case "game-time" -> { - var value = ConfigUtils.getValue(section.get("value")); - return ((effect, condition) -> { - effect.setGameTime(effect.getGameTime() + value.get(condition.getPlayer(), condition.getArgs())); - }); - } - case "game-time-bonus", "game-time-multiplier" -> { - var value = ConfigUtils.getValue(section.get("value")); - return ((effect, condition) -> { - effect.setGameTimeMultiplier(effect.getGameTimeMultiplier() + value.get(condition.getPlayer(), condition.getArgs()) - 1); - }); - } - case "lava-fishing" -> { - return ((effect, condition) -> effect.setLavaFishing(true)); - } - case "conditional" -> { - Requirement[] requirements = plugin.getRequirementManager().getRequirements(section.getConfigurationSection("conditions"), true); - EffectModifier[] modifiers = getEffectModifiers(section.getConfigurationSection("effects")); - return ((effect, condition) -> { - for (Requirement requirement : requirements) - if (!requirement.isConditionMet(condition)) - return; - for (EffectModifier modifier : modifiers) { - modifier.modify(effect, condition); - } - }); - } - default -> { - LogUtils.warn("Effect " + type + " doesn't exist."); - 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 deleted file mode 100644 index d1c7f108..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/entity/EntityManagerImpl.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.entity; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.manager.EntityManager; -import net.momirealms.customfishing.api.mechanic.entity.EntityConfig; -import net.momirealms.customfishing.api.mechanic.entity.EntityLibrary; -import net.momirealms.customfishing.api.mechanic.loot.Loot; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.compatibility.entity.VanillaEntityImpl; -import org.bukkit.Location; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Entity; -import org.bukkit.util.Vector; - -import java.io.File; -import java.util.*; - -public class EntityManagerImpl implements EntityManager { - - private final CustomFishingPlugin plugin; - private final HashMap entityLibraryMap; - private final HashMap entityConfigMap; - - public EntityManagerImpl(CustomFishingPlugin plugin) { - this.plugin = plugin; - this.entityLibraryMap = new HashMap<>(); - this.entityConfigMap = new HashMap<>(); - this.registerEntityLibrary(new VanillaEntityImpl()); - } - - public void load() { - this.loadConfig(); - } - - public void unload() { - HashMap tempMap = new HashMap<>(this.entityConfigMap); - this.entityConfigMap.clear(); - for (Map.Entry entry : tempMap.entrySet()) { - if (entry.getValue().isPersist()) { - tempMap.put(entry.getKey(), entry.getValue()); - } - } - } - - /** - * 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; - else entityLibraryMap.put(entityLibrary.identification(), entityLibrary); - 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 identification) { - return entityLibraryMap.remove(identification) != null; - } - - /** - * Load configuration files for entity properties. - */ - @SuppressWarnings("DuplicatedCode") - private void loadConfig() { - Deque fileDeque = new ArrayDeque<>(); - for (String type : List.of("entity")) { - File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type); - if (!typeFolder.exists()) { - if (!typeFolder.mkdirs()) return; - plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false); - } - fileDeque.push(typeFolder); - while (!fileDeque.isEmpty()) { - File file = fileDeque.pop(); - File[] files = file.listFiles(); - if (files == null) continue; - for (File subFile : files) { - if (subFile.isDirectory()) { - fileDeque.push(subFile); - } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { - this.loadSingleFile(subFile); - } - } - } - } - } - - /** - * Load a single entity configuration file. - * - * @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) { - String entityID = section.getString("entity"); - if (entityID == null) { - LogUtils.warn("Entity can't be null. File:" + file.getAbsolutePath() + "; Section:" + section.getCurrentPath()); - continue; - } - HashMap propertyMap = new HashMap<>(); - ConfigurationSection property = section.getConfigurationSection("properties"); - if (property != null) { - propertyMap.putAll(property.getValues(false)); - } - EntityConfig entityConfig = new EntityConfig.Builder() - .entityID(entityID) - .persist(false) - .horizontalVector(section.getDouble("velocity.horizontal", 1.1)) - .verticalVector(section.getDouble("velocity.vertical", 1.2)) - .propertyMap(propertyMap) - .build(); - entityConfigMap.put(entry.getKey(), entityConfig); - } - } - } - - public void disable() { - unload(); - this.entityConfigMap.clear(); - 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()); - if (config == null) { - LogUtils.warn("Entity: " + loot.getID() + " doesn't exist."); - return; - } - String entityID = config.getEntityID(); - Entity entity; - if (entityID.contains(":")) { - String[] split = entityID.split(":", 2); - String identification = split[0]; - String id = split[1]; - EntityLibrary library = entityLibraryMap.get(identification); - entity = library.spawn(hookLocation, id, config.getPropertyMap()); - } else { - entity = entityLibraryMap.get("vanilla").spawn(hookLocation, entityID, config.getPropertyMap()); - } - Vector vector = playerLocation.subtract(hookLocation).toVector().multiply((config.getHorizontalVector()) - 1); - vector = vector.setY((vector.getY() + 0.2) * config.getVerticalVector()); - entity.setVelocity(vector); - } -} 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 deleted file mode 100644 index 8e7e6381..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/BaitAnimationTask.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.fishing; - -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import net.momirealms.customfishing.util.FakeItemUtils; -import org.bukkit.entity.FishHook; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -/** - * A task responsible for animating bait when it's attached to a fishing hook. - */ -public class BaitAnimationTask implements Runnable { - - private final CancellableTask cancellableTask; - private final int entityID; - private final Player player; - private final FishHook fishHook; - - /** - * Constructs a new BaitAnimationTask. - * - * @param plugin The CustomFishingPlugin instance. - * @param player The player who cast the fishing rod. - * @param fishHook The FishHook entity. - * @param baitItem The bait ItemStack. - */ - public BaitAnimationTask(CustomFishingPlugin plugin, Player player, FishHook fishHook, ItemStack baitItem) { - this.player = player; - this.fishHook = fishHook; - entityID = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE); - if (plugin.getVersionManager().isVersionNewerThan1_19_R3()) { - CustomFishingPluginImpl.sendPackets(player, FakeItemUtils.getSpawnPacket(entityID, fishHook.getLocation()), FakeItemUtils.getMetaPacket(entityID, baitItem)); - } else { - CustomFishingPluginImpl.sendPacket(player, FakeItemUtils.getSpawnPacket(entityID, fishHook.getLocation())); - CustomFishingPluginImpl.sendPacket(player, FakeItemUtils.getMetaPacket(entityID, baitItem)); - } - this.cancellableTask = plugin.getScheduler().runTaskAsyncTimer(this, 50, 50, TimeUnit.MILLISECONDS); - } - - @Override - public void run() { - if ( fishHook == null - || fishHook.isOnGround() - || fishHook.isInLava() - || fishHook.isInWater() - || !fishHook.isValid() - ) { - cancelAnimation(); - } else { - CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getVelocityPacket(entityID, fishHook.getVelocity())); - CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getTpPacket(entityID, fishHook.getLocation())); - } - } - - /** - * Cancels the bait animation and cleans up resources. - */ - private void cancelAnimation() { - cancellableTask.cancel(); - CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, FakeItemUtils.getDestroyPacket(entityID)); - } -} 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 deleted file mode 100644 index a760a84c..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java +++ /dev/null @@ -1,883 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.fishing; - -import com.destroystokyo.paper.event.player.PlayerJumpEvent; -import de.tr7zw.changeme.nbtapi.NBTCompound; -import de.tr7zw.changeme.nbtapi.NBTItem; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.event.FishingResultEvent; -import net.momirealms.customfishing.api.event.LavaFishingEvent; -import net.momirealms.customfishing.api.event.RodCastEvent; -import net.momirealms.customfishing.api.manager.FishingManager; -import net.momirealms.customfishing.api.manager.RequirementManager; -import net.momirealms.customfishing.api.mechanic.GlobalSettings; -import net.momirealms.customfishing.api.mechanic.TempFishingState; -import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; -import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.condition.FishingPreparation; -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; -import net.momirealms.customfishing.api.mechanic.game.BasicGameConfig; -import net.momirealms.customfishing.api.mechanic.game.GameInstance; -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.util.LogUtils; -import net.momirealms.customfishing.api.util.WeightUtils; -import net.momirealms.customfishing.mechanic.requirement.RequirementManagerImpl; -import net.momirealms.customfishing.setting.CFConfig; -import net.momirealms.customfishing.util.ItemUtils; -import org.bukkit.*; -import org.bukkit.entity.*; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.*; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.ItemStack; -import org.bukkit.persistence.PersistentDataType; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -public class FishingManagerImpl implements Listener, FishingManager { - - private final CustomFishingPluginImpl plugin; - private final ConcurrentHashMap hookCacheMap; - private final ConcurrentHashMap hookCheckMap; - private final ConcurrentHashMap tempFishingStateMap; - private final ConcurrentHashMap gamingPlayerMap; - private final ConcurrentHashMap> vanillaLootMap; - - public FishingManagerImpl(CustomFishingPluginImpl plugin) { - this.plugin = plugin; - this.hookCacheMap = new ConcurrentHashMap<>(); - this.tempFishingStateMap = new ConcurrentHashMap<>(); - this.gamingPlayerMap = new ConcurrentHashMap<>(); - this.hookCheckMap = new ConcurrentHashMap<>(); - this.vanillaLootMap = new ConcurrentHashMap<>(); - } - - public void load() { - Bukkit.getPluginManager().registerEvents(this, plugin); - } - - public void unload() { - HandlerList.unregisterAll(this); - for (FishHook hook : hookCacheMap.values()) { - hook.remove(); - } - for (HookCheckTimerTask task : hookCheckMap.values()) { - task.destroy(); - } - for (GamingPlayer gamingPlayer : gamingPlayerMap.values()) { - gamingPlayer.cancel(); - } - this.hookCacheMap.clear(); - this.tempFishingStateMap.clear(); - this.gamingPlayerMap.clear(); - this.hookCheckMap.clear(); - } - - public void disable() { - unload(); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onFishMONITOR(PlayerFishEvent event) { - if (CFConfig.eventPriority != EventPriority.MONITOR) return; - this.selectState(event); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onFishHIGHEST(PlayerFishEvent event) { - if (CFConfig.eventPriority != EventPriority.HIGHEST) return; - this.selectState(event); - } - - @EventHandler(priority = EventPriority.HIGH) - public void onFishHIGH(PlayerFishEvent event) { - if (CFConfig.eventPriority != EventPriority.HIGH) return; - this.selectState(event); - } - - @EventHandler(priority = EventPriority.NORMAL) - public void onFishNORMAL(PlayerFishEvent event) { - if (CFConfig.eventPriority != EventPriority.NORMAL) return; - this.selectState(event); - } - - @EventHandler(priority = EventPriority.LOW) - public void onFishLOW(PlayerFishEvent event) { - if (CFConfig.eventPriority != EventPriority.LOW) return; - this.selectState(event); - } - - @EventHandler(priority = EventPriority.LOWEST) - public void onFishLOWEST(PlayerFishEvent event) { - if (CFConfig.eventPriority != EventPriority.LOWEST) return; - this.selectState(event); - } - - @EventHandler - public void onQuit(PlayerQuitEvent event) { - final Player player = event.getPlayer(); - final UUID uuid = player.getUniqueId(); - this.removeHook(uuid); - this.removeTempFishingState(player); - this.removeHookCheckTask(player); - this.vanillaLootMap.remove(uuid); - GamingPlayer gamingPlayer = gamingPlayerMap.remove(player.getUniqueId()); - if (gamingPlayer != null) { - gamingPlayer.cancel(); - } - } - - /** - * Known bug: This is a Minecraft packet limitation - * When you fish, both left click air and right click air - * are triggered. And you can't cancel the left click event. - */ - @EventHandler (ignoreCancelled = false) - public void onLeftClick(PlayerInteractEvent event) { - if (event.getAction() != Action.LEFT_CLICK_AIR) - return; - if (event.getMaterial() != Material.FISHING_ROD) - return; - if (event.getHand() != EquipmentSlot.HAND) - return; - GamingPlayer gamingPlayer = gamingPlayerMap.get(event.getPlayer().getUniqueId()); - if (gamingPlayer != null) { - if (gamingPlayer.onLeftClick()) - event.setCancelled(true); - } - } - - @EventHandler (ignoreCancelled = true) - public void onSwapHand(PlayerSwapHandItemsEvent event) { - GamingPlayer gamingPlayer = gamingPlayerMap.get(event.getPlayer().getUniqueId()); - if (gamingPlayer != null) { - if (gamingPlayer.onSwapHand()) - event.setCancelled(true); - } - } - - @EventHandler - public void onJump(PlayerJumpEvent event) { - if (event.isCancelled()) return; - GamingPlayer gamingPlayer = gamingPlayerMap.get(event.getPlayer().getUniqueId()); - if (gamingPlayer != null) { - if (gamingPlayer.onJump()) - event.setCancelled(true); - } - } - - @EventHandler - public void onSneak(PlayerToggleSneakEvent event) { - if (event.isCancelled()) return; - if (!event.isSneaking()) return; - GamingPlayer gamingPlayer = gamingPlayerMap.get(event.getPlayer().getUniqueId()); - if (gamingPlayer != null) { - if (gamingPlayer.onSneak()) - event.setCancelled(true); - } - } - - @EventHandler - public void onChat(AsyncPlayerChatEvent event) { - if (event.isCancelled()) return; - GamingPlayer gamingPlayer = gamingPlayerMap.get(event.getPlayer().getUniqueId()); - if (gamingPlayer != null) { - if (gamingPlayer.onChat(event.getMessage())) - event.setCancelled(true); - } - } - - /** - * 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); - if (hook != null && hook.isValid()) { - plugin.getScheduler().runTaskSync(hook::remove, hook.getLocation()); - return true; - } else { - return false; - } - } - - /** - * 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 - @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()) { - case FISHING -> onCastRod(event); - case REEL_IN -> onReelIn(event); - case CAUGHT_ENTITY -> onCaughtEntity(event); - case CAUGHT_FISH -> onCaughtFish(event); - case BITE -> onBite(event); - case IN_GROUND -> onInGround(event); - } - } - - /** - * Handle the event when the fishing hook lands on the ground. - * - * @param event The PlayerFishEvent that occurred. - */ - private void onInGround(PlayerFishEvent event) { - final Player player = event.getPlayer(); - FishHook hook = event.getHook(); - if (player.getGameMode() != GameMode.CREATIVE) { - ItemStack itemStack = player.getInventory().getItemInMainHand(); - if (itemStack.getType() != Material.FISHING_ROD) itemStack = player.getInventory().getItemInOffHand(); - if (itemStack.getType() == Material.FISHING_ROD) { - NBTItem nbtItem = new NBTItem(itemStack); - NBTCompound compound = nbtItem.getCompound("CustomFishing"); - if (compound != null && compound.hasTag("max_dur")) { - event.setCancelled(true); - hook.remove(); - ItemUtils.decreaseDurability(player, itemStack, 2, true); - } - } - } - } - - /** - * Handle the event when a player casts a fishing rod. - * - * @param event The PlayerFishEvent that occurred. - */ - public void onCastRod(PlayerFishEvent event) { - var player = event.getPlayer(); - var fishingPreparation = new FishingPreparationImpl(player, plugin); - if (!fishingPreparation.canFish()) { - event.setCancelled(true); - return; - } - // Check mechanic requirements - if (!RequirementManager.isRequirementMet( - fishingPreparation, RequirementManagerImpl.mechanicRequirements - )) { - this.removeTempFishingState(player); - return; - } - FishingEffect initialEffect = plugin.getEffectManager().getInitialEffect(); - // Merge totem effects - EffectCarrier totemEffect = plugin.getTotemManager().getTotemEffect(player.getLocation()); - if (totemEffect != null) - for (EffectModifier modifier : totemEffect.getEffectModifiers()) { - modifier.modify(initialEffect, fishingPreparation); - } - - // Call custom event - RodCastEvent rodCastEvent = new RodCastEvent(event, fishingPreparation, initialEffect); - Bukkit.getPluginManager().callEvent(rodCastEvent); - if (rodCastEvent.isCancelled()) { - return; - } - - // Store fishhook entity and apply the effects - final FishHook fishHook = event.getHook(); - this.hookCacheMap.put(player.getUniqueId(), fishHook); - - // Reduce amount & Send animation - var baitItem = fishingPreparation.getBaitItemStack(); - if (baitItem != null) { - ItemStack cloned = baitItem.clone(); - cloned.setAmount(1); - new BaitAnimationTask(plugin, player, fishHook, cloned); - baitItem.setAmount(baitItem.getAmount() - 1); - } - - // Arrange hook check task - this.hookCheckMap.put(player.getUniqueId(), new HookCheckTimerTask(this, fishHook, fishingPreparation, initialEffect)); - // trigger actions - fishingPreparation.triggerActions(ActionTrigger.CAST); - } - - /** - * Handle the event when a player catches an entity. - * - * @param event The PlayerFishEvent that occurred. - */ - private void onCaughtEntity(PlayerFishEvent event) { - final Player player = event.getPlayer(); - final UUID uuid = player.getUniqueId(); - - Entity entity = event.getCaught(); - if ((entity instanceof ArmorStand armorStand) - && armorStand.getPersistentDataContainer().get( - Objects.requireNonNull(NamespacedKey.fromString("lavafishing", plugin)), - PersistentDataType.STRING - ) != null) { - // The hook is hooked into the temp entity - // This might be called both not in game and in game - LavaFishingEvent lavaFishingEvent = new LavaFishingEvent(player, LavaFishingEvent.State.REEL_IN, event.getHook()); - Bukkit.getPluginManager().callEvent(lavaFishingEvent); - if (lavaFishingEvent.isCancelled()) { - event.setCancelled(true); - return; - } - - GamingPlayer gamingPlayer = gamingPlayerMap.get(uuid); - if (gamingPlayer != null) { - // in game - if (gamingPlayer.onRightClick()) - event.setCancelled(true); - } else { - // not in game - HookCheckTimerTask task = hookCheckMap.get(uuid); - if (task != null) - task.destroy(); - else - // should not reach this but in case - entity.remove(); - } - return; - } - - if (player.getGameMode() != GameMode.CREATIVE) { - ItemStack itemStack = player.getInventory().getItemInMainHand(); - if (itemStack.getType() != Material.FISHING_ROD) itemStack = player.getInventory().getItemInOffHand(); - NBTItem nbtItem = new NBTItem(itemStack); - NBTCompound nbtCompound = nbtItem.getCompound("CustomFishing"); - if (nbtCompound != null && nbtCompound.hasTag("max_dur")) { - event.getHook().remove(); - event.setCancelled(true); - ItemUtils.decreaseDurability(player, itemStack, 5, true); - } - } - } - - /** - * Handle the event when a player catches a fish. - * - * @param event The PlayerFishEvent that occurred. - */ - private void onCaughtFish(PlayerFishEvent event) { - final Player player = event.getPlayer(); - final UUID uuid = player.getUniqueId(); - if (!(event.getCaught() instanceof Item item)) - return; - - // If player is playing the game - GamingPlayer gamingPlayer = gamingPlayerMap.get(uuid); - if (gamingPlayer != null) { - if (gamingPlayer.onRightClick()) - event.setCancelled(true); - return; - } - - // If player is not playing the game - var temp = this.getTempFishingState(uuid); - if (temp != null) { - var loot = temp.getLoot(); - if (loot.getID().equals("vanilla")) { - // put vanilla loot in map - this.vanillaLootMap.put(uuid, Pair.of(item.getItemStack(), event.getExpToDrop())); - } - var fishingPreparation = temp.getPreparation(); - loot.triggerActions(ActionTrigger.HOOK, fishingPreparation); - fishingPreparation.triggerActions(ActionTrigger.HOOK); - if (!loot.disableGame()) { - // start the game if the loot has a game - if (startFishingGame(player, fishingPreparation, temp.getEffect())) { - event.setCancelled(true); - } - } else { - // remove temp state if fishing game not exists - this.removeTempFishingState(player); - var hook = event.getHook(); - // If the game is disabled, then do success actions - success(temp, hook); - // Cancel the event because loots can be multiple and unique - event.setCancelled(true); - hook.remove(); - } - return; - } - } - - /** - * Handle the event when a player receives a bite on their fishing hook. - * - * @param event The PlayerFishEvent that occurred. - */ - private void onBite(PlayerFishEvent event) { - final Player player = event.getPlayer(); - final UUID uuid = player.getUniqueId(); - - // If player is already in game - // then ignore the event - GamingPlayer gamingPlayer = getGamingPlayer(uuid); - if (gamingPlayer != null) { - return; - } - - // If the loot's game is instant - TempFishingState temp = getTempFishingState(uuid); - if (temp != null) { - var loot = temp.getLoot(); - var fishingPreparation = temp.getPreparation(); - fishingPreparation.setLocation(event.getHook().getLocation()); - - if (!loot.disableGlobalAction()) - GlobalSettings.triggerLootActions(ActionTrigger.BITE, fishingPreparation); - loot.triggerActions(ActionTrigger.BITE, fishingPreparation); - fishingPreparation.triggerActions(ActionTrigger.BITE); - - if (loot.instanceGame() && !loot.disableGame()) { - if (!loot.disableGlobalAction()) - GlobalSettings.triggerLootActions(ActionTrigger.HOOK, fishingPreparation); - loot.triggerActions(ActionTrigger.HOOK, fishingPreparation); - fishingPreparation.triggerActions(ActionTrigger.HOOK); - startFishingGame(player, fishingPreparation, temp.getEffect()); - } - } - } - - /** - * Handle the event when a player reels in their fishing line. - * - * @param event The PlayerFishEvent that occurred. - */ - private void onReelIn(PlayerFishEvent event) { - final Player player = event.getPlayer(); - final UUID uuid = player.getUniqueId(); - - // If player is in game - GamingPlayer gamingPlayer = getGamingPlayer(uuid); - if (gamingPlayer != null) { - if (gamingPlayer.onRightClick()) - event.setCancelled(true); - return; - } - - // If player is lava fishing - HookCheckTimerTask hookTask = hookCheckMap.get(uuid); - if (hookTask != null && hookTask.isFishHooked()) { - LavaFishingEvent lavaFishingEvent = new LavaFishingEvent(player, LavaFishingEvent.State.CAUGHT_FISH, event.getHook()); - Bukkit.getPluginManager().callEvent(lavaFishingEvent); - if (lavaFishingEvent.isCancelled()) { - event.setCancelled(true); - return; - } - - var temp = getTempFishingState(uuid); - if (temp != null ) { - Loot loot = temp.getLoot(); - var fishingPreparation = temp.getPreparation(); - if (!loot.disableGlobalAction()) - GlobalSettings.triggerLootActions(ActionTrigger.HOOK, fishingPreparation); - loot.triggerActions(ActionTrigger.HOOK, fishingPreparation); - fishingPreparation.triggerActions(ActionTrigger.HOOK); - if (!loot.disableGame()) { - event.setCancelled(true); - startFishingGame(player, fishingPreparation, temp.getEffect()); - } else { - success(temp, event.getHook()); - } - } - return; - } - } - - /** - * Removes the temporary fishing state associated with a player. - * - * @param player The player whose temporary fishing state should be removed. - */ - @Override - 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(); - final UUID uuid = player.getUniqueId(); - FishHook fishHook = hookCacheMap.remove(uuid); - if (fishHook == null) { - LogUtils.warn("Unexpected situation: Can't get player's fish hook when processing game results."); - return; - } - TempFishingState tempFishingState = removeTempFishingState(player); - if (tempFishingState == null) { - LogUtils.warn("Unexpected situation: Can't get player's fishing state when processing game results."); - return; - } - Effect bonus = gamingPlayer.getEffectReward(); - if (bonus != null) - tempFishingState.getEffect().merge(bonus); - - gamingPlayer.cancel(); - gamingPlayerMap.remove(uuid); - plugin.getScheduler().runTaskSync(() -> { - - if (gamingPlayer.isSuccessful()) { - success(tempFishingState, fishHook); - } else { - fail(tempFishingState, fishHook); - } - - fishHook.remove(); - - }, fishHook.getLocation()); - } - - public void fail(TempFishingState state, FishHook hook) { - var loot = state.getLoot(); - var fishingPreparation = state.getPreparation(); - - if (loot.getID().equals("vanilla")) { - Pair pair = this.vanillaLootMap.remove(fishingPreparation.getPlayer().getUniqueId()); - if (pair != null) { - fishingPreparation.insertArg("{nick}", ""); - fishingPreparation.insertArg("{loot}", pair.left().getType().toString()); - } - } - - // call event - FishingResultEvent fishingResultEvent = new FishingResultEvent( - fishingPreparation.getPlayer(), - FishingResultEvent.Result.FAILURE, - hook, - loot, - fishingPreparation.getArgs() - ); - Bukkit.getPluginManager().callEvent(fishingResultEvent); - if (fishingResultEvent.isCancelled()) { - return; - } - - if (!loot.disableGlobalAction()) - GlobalSettings.triggerLootActions(ActionTrigger.FAILURE, fishingPreparation); - loot.triggerActions(ActionTrigger.FAILURE, fishingPreparation); - fishingPreparation.triggerActions(ActionTrigger.FAILURE); - - if (state.getPreparation().getPlayer().getGameMode() != GameMode.CREATIVE) { - ItemUtils.decreaseHookDurability(fishingPreparation.getRodItemStack(), 1, true); - } - } - - /** - * Handle the success of a fishing attempt, including spawning loot, calling events, and executing success actions. - * - * @param state The temporary fishing state containing information about the loot and effect. - * @param hook The FishHook entity associated with the fishing attempt. - */ - public void success(TempFishingState state, FishHook hook) { - var loot = state.getLoot(); - var effect = state.getEffect(); - var fishingPreparation = state.getPreparation(); - var player = fishingPreparation.getPlayer(); - fishingPreparation.insertArg("{size-multiplier}", String.valueOf(effect.getSizeMultiplier())); - fishingPreparation.insertArg("{size-fixed}", String.valueOf(effect.getSize())); - int amount; - if (loot.getType() == LootType.ITEM) { - amount = (int) effect.getMultipleLootChance(); - amount += Math.random() < (effect.getMultipleLootChance() - amount) ? 2 : 1; - } else { - amount = 1; - } - fishingPreparation.insertArg("{amount}", String.valueOf(amount)); - - // call event - FishingResultEvent fishingResultEvent = new FishingResultEvent( - player, - FishingResultEvent.Result.SUCCESS, - hook, - loot, - fishingPreparation.getArgs() - ); - Bukkit.getPluginManager().callEvent(fishingResultEvent); - if (fishingResultEvent.isCancelled()) { - return; - } - - switch (loot.getType()) { - case ITEM -> { - // build the items for multiple times instead of using setAmount() to make sure that each item is unique - if (loot.getID().equals("vanilla")) { - Pair pair = vanillaLootMap.remove(player.getUniqueId()); - if (pair != null) { - fishingPreparation.insertArg("{nick}", ""); - for (int i = 0; i < amount; i++) { - plugin.getScheduler().runTaskSyncLater(() -> { - plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), pair.left().clone(), fishingPreparation); - doSuccessActions(loot, effect, fishingPreparation, player); - if (pair.right() > 0) { - player.giveExp(pair.right(), true); - AdventureHelper.getInstance().sendSound(player, Sound.Source.PLAYER, Key.key("minecraft:entity.experience_orb.pickup"), 1, 1); - } - }, hook.getLocation(), (long) CFConfig.multipleLootSpawnDelay * i); - } - } - } else { - for (int i = 0; i < amount; i++) { - plugin.getScheduler().runTaskSyncLater(() -> { - ItemStack item = plugin.getItemManager().build(player, "item", loot.getID(), fishingPreparation.getArgs()); - if (item == null) { - LogUtils.warn(String.format("Item %s not exists", loot.getID())); - return; - } - plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), item, fishingPreparation); - doSuccessActions(loot, effect, fishingPreparation, player); - }, hook.getLocation(), (long) CFConfig.multipleLootSpawnDelay * i); - } - } - } - case ENTITY -> { - plugin.getEntityManager().summonEntity(hook.getLocation(), player.getLocation(), loot); - doSuccessActions(loot, effect, fishingPreparation, player); - } - case BLOCK -> { - plugin.getBlockManager().summonBlock(player, hook.getLocation(), player.getLocation(), loot); - doSuccessActions(loot, effect, fishingPreparation, player); - } - } - - if (player.getGameMode() != GameMode.CREATIVE) { - ItemStack rod = state.getPreparation().getRodItemStack(); - ItemUtils.decreaseHookDurability(rod, 1, false); - ItemUtils.decreaseDurability(player, rod, 1, true); - } - } - - /** - * Execute success-related actions after a successful fishing attempt, including updating competition data, triggering events and actions, and updating player statistics. - * - * @param loot The loot that was successfully caught. - * @param effect The effect applied during fishing. - * @param fishingPreparation The fishing preparation containing preparation data. - * @param player The player who successfully caught the loot. - */ - private void doSuccessActions(Loot loot, Effect effect, FishingPreparation fishingPreparation, Player player) { - FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); - if (competition != null && RequirementManager.isRequirementMet(fishingPreparation, competition.getConfig().getRequirements())) { - String scoreStr = fishingPreparation.getArg("{CUSTOM_SCORE}"); - if (scoreStr != null) { - competition.refreshData(player, Double.parseDouble(scoreStr)); - } else { - double score = 0; - switch (competition.getGoal()) { - case CATCH_AMOUNT -> { - score = 1; - competition.refreshData(player, score); - } - case MAX_SIZE, TOTAL_SIZE -> { - String size = fishingPreparation.getArg("{SIZE}"); - if (size != null) { - score = Double.parseDouble(size); - competition.refreshData(player, score); - } else { - score = 0; - } - } - case TOTAL_SCORE -> { - score = loot.getScore(); - if (score > 0) { - score = score * effect.getScoreMultiplier() + effect.getScore(); - competition.refreshData(player, score); - } else { - score = 0; - } - } - } - fishingPreparation.insertArg("{score}", String.format("%.2f", score)); - fishingPreparation.insertArg("{SCORE}", String.valueOf(score)); - } - } - - // events and actions - if (!loot.disableGlobalAction()) - GlobalSettings.triggerLootActions(ActionTrigger.SUCCESS, fishingPreparation); - loot.triggerActions(ActionTrigger.SUCCESS, fishingPreparation); - fishingPreparation.triggerActions(ActionTrigger.SUCCESS); - - player.setStatistic( - Statistic.FISH_CAUGHT, - player.getStatistic(Statistic.FISH_CAUGHT) + 1 - ); - - if (!loot.disableStats()) - Optional.ofNullable( - plugin.getStatisticsManager() - .getStatistics(player.getUniqueId()) - ).ifPresent(it -> { - it.addLootAmount(loot, fishingPreparation, 1); - String size = fishingPreparation.getArg("{SIZE}"); - if (size != null) - if (it.setSizeIfHigher(loot.getStatisticKey().getSizeKey(), Float.parseFloat(size))) { - if (!loot.disableGlobalAction()) - GlobalSettings.triggerLootActions(ActionTrigger.NEW_SIZE_RECORD, fishingPreparation); - loot.triggerActions(ActionTrigger.NEW_SIZE_RECORD, fishingPreparation); - } - }); - } - - /** - * 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 boolean startFishingGame(Player player, Condition condition, Effect effect) { - Map gameWithWeight = plugin.getGameManager().getGameWithWeight(condition); - String random = WeightUtils.getRandom(gameWithWeight); - Pair gamePair = plugin.getGameManager().getGameInstance(random); - if (random == null) { - LogUtils.warn("No game is available for player:" + player.getName() + " location:" + condition.getLocation()); - return false; - } - if (gamePair == null) { - LogUtils.warn(String.format("Game %s doesn't exist.", random)); - return false; - } - plugin.debug("Game: " + random); - return 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 boolean startFishingGame(Player player, GameSettings settings, GameInstance gameInstance) { - plugin.debug("Difficulty:" + settings.getDifficulty()); - plugin.debug("Time:" + settings.getTime()); - FishHook hook = getHook(player.getUniqueId()); - if (hook != null) { - this.gamingPlayerMap.put(player.getUniqueId(), gameInstance.start(player, hook, settings)); - return true; - } else { - LogUtils.warn("It seems that player " + player.getName() + " is not fishing. Fishing game failed to start."); - return false; - } - } - - /** - * 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); - if (fishHook == null) return false; - if (!fishHook.isValid()) { - hookCacheMap.remove(uuid); - return false; - } - 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); - } - - 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/FishingPreparationImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingPreparationImpl.java deleted file mode 100644 index 76a54c58..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingPreparationImpl.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.fishing; - -import de.tr7zw.changeme.nbtapi.NBTCompound; -import de.tr7zw.changeme.nbtapi.NBTItem; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.mechanic.GlobalSettings; -import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; -import net.momirealms.customfishing.api.mechanic.condition.FishingPreparation; -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.Material; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -public class FishingPreparationImpl extends FishingPreparation { - - private boolean hasBait = false; - private boolean hasHook = false; - private @Nullable ItemStack baitItemStack; - private final @NotNull ItemStack rodItemStack; - private final List effects; - private boolean canFish = true; - - public FishingPreparationImpl(Player player, CustomFishingPlugin plugin) { - super(player); - - PlayerInventory playerInventory = player.getInventory(); - ItemStack mainHandItem = playerInventory.getItemInMainHand(); - ItemStack offHandItem = playerInventory.getItemInOffHand(); - - this.effects = new ArrayList<>(); - boolean rodOnMainHand = mainHandItem.getType() == Material.FISHING_ROD; - this.rodItemStack = rodOnMainHand ? mainHandItem : offHandItem; - String rodItemID = plugin.getItemManager().getAnyPluginItemID(this.rodItemStack); - EffectCarrier rodEffect = plugin.getEffectManager().getEffectCarrier("rod", rodItemID); - if (rodEffect != null) effects.add(rodEffect); - super.insertArg("{rod}", rodItemID); - - NBTItem nbtItem = new NBTItem(rodItemStack); - NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); - if (cfCompound != null && cfCompound.hasTag("hook_id")) { - String hookID = cfCompound.getString("hook_id"); - super.insertArg("{hook}", hookID); - this.hasHook = true; - EffectCarrier carrier = plugin.getEffectManager().getEffectCarrier("hook", hookID); - if (carrier != null) { - this.effects.add(carrier); - } - } - - String baitItemID = plugin.getItemManager().getAnyPluginItemID(rodOnMainHand ? offHandItem : mainHandItem); - EffectCarrier baitEffect = plugin.getEffectManager().getEffectCarrier("bait", baitItemID); - - if (baitEffect != null) { - this.baitItemStack = rodOnMainHand ? offHandItem : mainHandItem; - this.effects.add(baitEffect); - this.hasBait = true; - super.insertArg("{bait}", baitItemID); - } - - if (plugin.getBagManager().isEnabled()) { - Inventory fishingBag = plugin.getBagManager().getOnlineBagInventory(player.getUniqueId()); - HashSet uniqueUtils = new HashSet<>(4); - if (fishingBag != null) { - this.insertArg("{in-bag}", "true"); - for (int i = 0; i < fishingBag.getSize(); i++) { - ItemStack itemInBag = fishingBag.getItem(i); - String bagItemID = plugin.getItemManager().getAnyPluginItemID(itemInBag); - if (!hasBait) { - EffectCarrier effect = plugin.getEffectManager().getEffectCarrier("bait", bagItemID); - if (effect != null) { - this.hasBait = true; - this.baitItemStack = itemInBag; - this.effects.add(effect); - super.insertArg("{bait}", bagItemID); - continue; - } - } - EffectCarrier utilEffect = plugin.getEffectManager().getEffectCarrier("util", bagItemID); - if (utilEffect != null && !uniqueUtils.contains(bagItemID)) { - effects.add(utilEffect); - uniqueUtils.add(bagItemID); - } - } - this.delArg("{in-bag}"); - } - } - - for (String enchant : plugin.getIntegrationManager().getEnchantments(rodItemStack)) { - EffectCarrier enchantEffect = plugin.getEffectManager().getEffectCarrier("enchant", enchant); - if (enchantEffect != null) { - this.effects.add(enchantEffect); - } - } - - for (EffectCarrier effectCarrier : effects) { - if (!effectCarrier.isConditionMet(this)) { - this.canFish = false; - return; - } - } - } - - /** - * Retrieves the ItemStack representing the fishing rod. - * - * @return The ItemStack representing the fishing rod. - */ - @NotNull - public ItemStack getRodItemStack() { - return rodItemStack; - } - - /** - * Retrieves the ItemStack representing the bait (if available). - * - * @return The ItemStack representing the bait, or null if no bait is set. - */ - @Nullable - public ItemStack getBaitItemStack() { - return baitItemStack; - } - - /** - * Checks if player meet the requirements for fishing gears - * - * @return True if can fish, false otherwise. - */ - public boolean canFish() { - return this.canFish; - } - - /** - * Merges a FishingEffect into this fishing rod, applying effect modifiers. - * - * @param effect The FishingEffect to merge into this rod. - */ - public void mergeEffect(FishingEffect effect) { - for (EffectModifier modifier : GlobalSettings.getEffectModifiers()) { - modifier.modify(effect, this); - } - for (EffectCarrier effectCarrier : effects) { - for (EffectModifier modifier : effectCarrier.getEffectModifiers()) { - modifier.modify(effect, this); - } - } - } - - /** - * Triggers actions associated with a specific action trigger. - * - * @param actionTrigger The action trigger that initiates the actions. - */ - public void triggerActions(ActionTrigger actionTrigger) { - GlobalSettings.triggerRodActions(actionTrigger, this); - if (hasBait) GlobalSettings.triggerBaitActions(actionTrigger, this); - if (hasHook) GlobalSettings.triggerHookActions(actionTrigger, this); - for (EffectCarrier effectCarrier : effects) { - Action[] actions = effectCarrier.getActions(actionTrigger); - if (actions != null) - for (Action action : actions) { - action.trigger(this); - } - } - } -} 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 deleted file mode 100644 index 09fffc25..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.fishing; - -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.event.FishHookLandEvent; -import net.momirealms.customfishing.api.event.LavaFishingEvent; -import net.momirealms.customfishing.api.mechanic.TempFishingState; -import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; -import net.momirealms.customfishing.api.mechanic.condition.FishingPreparation; -import net.momirealms.customfishing.api.mechanic.effect.Effect; -import net.momirealms.customfishing.api.mechanic.effect.FishingEffect; -import net.momirealms.customfishing.api.mechanic.loot.Loot; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import net.momirealms.customfishing.setting.CFConfig; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.ArmorStand; -import org.bukkit.entity.Entity; -import org.bukkit.entity.FishHook; -import org.bukkit.persistence.PersistentDataType; -import org.bukkit.util.Vector; - -import java.util.Objects; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -/** - * A task responsible for checking the state of a fishing hook and handling lava fishing mechanics. - */ -public class HookCheckTimerTask implements Runnable { - - private final FishingManagerImpl manager; - private final CancellableTask hookMovementTask; - private LavaEffectTask lavaFishingTask; - private final FishHook fishHook; - private final FishingPreparation fishingPreparation; - private final FishingEffect initialEffect; - private Effect tempEffect; - private final int lureLevel; - private boolean firstTime; - private boolean fishHooked; - private boolean reserve; - private int jumpTimer; - private Entity hookedEntity; - private Loot loot; - private boolean inWater; - - /** - * Constructs a new HookCheckTimerTask. - * - * @param manager The FishingManagerImpl instance. - * @param fishHook The FishHook entity being checked. - * @param fishingPreparation The FishingPreparation instance. - * @param initialEffect The initial fishing effect. - */ - public HookCheckTimerTask( - FishingManagerImpl manager, - FishHook fishHook, - FishingPreparation fishingPreparation, - FishingEffect initialEffect - ) { - this.inWater = false; - this.manager = manager; - this.fishHook = fishHook; - this.initialEffect = initialEffect; - this.fishingPreparation = fishingPreparation; - this.hookMovementTask = CustomFishingPlugin.get().getScheduler().runTaskSyncTimer(this, fishHook.getLocation(), 1, 1); - this.lureLevel = fishingPreparation.getRodItemStack().getEnchantmentLevel(Enchantment.LURE); - this.firstTime = true; - this.tempEffect = new FishingEffect(); - } - - @Override - public void run() { - if ( - !this.fishHook.isValid() - //|| (fishHook.getHookedEntity() != null && fishHook.getHookedEntity().getType() != EntityType.ARMOR_STAND) - ) { - // This task would be cancelled when hook is removed - this.destroy(); - return; - } - if (this.fishHook.isOnGround()) { - this.inWater = false; - return; - } - if (this.fishHook.getLocation().getBlock().getType() == Material.LAVA) { - this.inWater = false; - // if player can fish in lava - if (firstTime) { - this.firstTime = false; - - this.fishingPreparation.setLocation(this.fishHook.getLocation()); - this.fishingPreparation.mergeEffect(this.initialEffect); - if (!initialEffect.canLavaFishing()) { - this.destroy(); - return; - } - - FishHookLandEvent event = new FishHookLandEvent(this.fishingPreparation.getPlayer(), FishHookLandEvent.Target.LAVA, this.fishHook, true, this.initialEffect); - Bukkit.getPluginManager().callEvent(event); - - this.fishingPreparation.insertArg("{lava}", "true"); - this.fishingPreparation.triggerActions(ActionTrigger.LAND); - } - - // simulate fishing mechanic - if (this.fishHooked) { - this.jumpTimer++; - if (this.jumpTimer < 4) - return; - this.jumpTimer = 0; - this.fishHook.setVelocity(new Vector(0,0.24,0)); - return; - } - - if (!this.reserve) { - // jump - if (this.jumpTimer < 5) { - this.jumpTimer++; - this.fishHook.setVelocity(new Vector(0,0.2 - this.jumpTimer * 0.02,0)); - return; - } - - this.reserve = true; - - this.setNextLoot(); - if (this.loot != null) { - this.tempEffect = this.loot.getBaseEffect().build(fishingPreparation.getPlayer(), fishingPreparation.getArgs()); - this.tempEffect.merge(this.initialEffect); - this.setTempState(); - this.startLavaFishingMechanic(); - } else { - this.tempEffect = new FishingEffect(); - this.tempEffect.merge(this.initialEffect); - this.manager.removeTempFishingState(fishingPreparation.getPlayer()); - CustomFishingPlugin.get().debug("No loot available for " + fishingPreparation.getPlayer().getName() + " at " + fishingPreparation.getLocation()); - } - - this.makeHookStatic(this.fishHook.getLocation()); - } - return; - } - if (!this.inWater && this.fishHook.isInWater()) { - this.inWater = true; - - this.fishingPreparation.setLocation(this.fishHook.getLocation()); - this.fishingPreparation.insertArg("{lava}", "false"); - this.fishingPreparation.insertArg("{open-water}", String.valueOf(this.fishHook.isInOpenWater())); - - if (this.firstTime) { - this.firstTime = false; - this.fishingPreparation.mergeEffect(this.initialEffect); - - FishHookLandEvent event = new FishHookLandEvent(this.fishingPreparation.getPlayer(), FishHookLandEvent.Target.WATER, this.fishHook, false, this.initialEffect); - Bukkit.getPluginManager().callEvent(event); - - this.fishingPreparation.triggerActions(ActionTrigger.LAND); - - } else { - FishHookLandEvent event = new FishHookLandEvent(this.fishingPreparation.getPlayer(), FishHookLandEvent.Target.WATER, this.fishHook, true, this.initialEffect); - Bukkit.getPluginManager().callEvent(event); - } - - this.setNextLoot(); - if (this.loot == null) { - // prevent players from getting vanilla loots - this.fishHook.setWaitTime(Integer.MAX_VALUE); - this.tempEffect = new FishingEffect(); - this.tempEffect.merge(this.initialEffect); - this.manager.removeTempFishingState(fishingPreparation.getPlayer()); - CustomFishingPlugin.get().debug("No loot available for " + fishingPreparation.getPlayer().getName() + " at " + fishingPreparation.getLocation()); - } else { - this.tempEffect = this.loot.getBaseEffect().build(fishingPreparation.getPlayer(), fishingPreparation.getArgs()); - this.tempEffect.merge(this.initialEffect); - this.setWaitTime(); - this.setTempState(); - } - - return; - } - } - - /** - * Destroys the task and associated entities. - */ - public void destroy() { - this.cancelSubTask(); - this.removeTempEntity(); - this.hookMovementTask.cancel(); - this.manager.removeHookCheckTask(fishingPreparation.getPlayer()); - } - - /** - * Cancels the lava fishing subtask if it's active. - */ - public void cancelSubTask() { - if (lavaFishingTask != null && !lavaFishingTask.isCancelled()) { - lavaFishingTask.cancel(); - lavaFishingTask = null; - } - } - - private void setNextLoot() { - Loot nextLoot = CustomFishingPlugin.get().getLootManager().getNextLoot(initialEffect, fishingPreparation); - if (nextLoot == null) { - this.loot = null; - return; - } - this.loot = nextLoot; - } - - /** - * Sets temporary state and prepares for the next loot. - */ - private void setTempState() { - fishingPreparation.insertArg("{nick}", loot.getNick()); - fishingPreparation.insertArg("{loot}", loot.getID()); - if (!loot.disableStats()) { - fishingPreparation.insertArg("{statistics_size}", loot.getStatisticKey().getSizeKey()); - fishingPreparation.insertArg("{statistics_amount}", loot.getStatisticKey().getAmountKey()); - } - manager.setTempFishingState(fishingPreparation.getPlayer(), new TempFishingState( - tempEffect, - fishingPreparation, - loot - )); - } - - /** - * Removes the temporary hooked entity. - */ - public void removeTempEntity() { - if (hookedEntity != null && !hookedEntity.isDead()) - hookedEntity.remove(); - } - - /** - * Starts the lava fishing mechanic. - */ - private void startLavaFishingMechanic() { - // get random time - int random; - if (CFConfig.overrideVanilla) { - random = ThreadLocalRandom.current().nextInt(CFConfig.lavaMinTime, CFConfig.lavaMaxTime); - random *= tempEffect.getWaitTimeMultiplier(); - random += tempEffect.getWaitTime(); - random = Math.max(1, random); - } else { - random = ThreadLocalRandom.current().nextInt(CFConfig.lavaMinTime, CFConfig.lavaMaxTime); - random -= lureLevel * 100; - random = Math.max(CFConfig.lavaMinTime, random); - random *= tempEffect.getWaitTimeMultiplier(); - random += tempEffect.getWaitTime(); - random = Math.max(1, random); - } - - // lava effect task (Three seconds in advance) - this.lavaFishingTask = new LavaEffectTask( - this, - fishHook.getLocation(), - random - 3 * 20 - ); - } - - /** - * Handles the hook state of the fish hook. - */ - public void getHooked() { - LavaFishingEvent lavaFishingEvent = new LavaFishingEvent(fishingPreparation.getPlayer(), LavaFishingEvent.State.BITE, fishHook); - Bukkit.getPluginManager().callEvent(lavaFishingEvent); - if (lavaFishingEvent.isCancelled()) { - this.startLavaFishingMechanic(); - return; - } - - this.loot.triggerActions(ActionTrigger.BITE, fishingPreparation); - this.fishingPreparation.triggerActions(ActionTrigger.BITE); - - this.fishHooked = true; - this.removeTempEntity(); - - AdventureHelper.getInstance().sendSound( - fishingPreparation.getPlayer(), - Sound.Source.NEUTRAL, - Key.key("minecraft:block.pointed_dripstone.drip_lava_into_cauldron"), - 1, - 1 - ); - - CustomFishingPlugin.get().getScheduler().runTaskAsyncLater(() -> { - fishHooked = false; - reserve = false; - }, (2 * 20) * 50L, TimeUnit.MILLISECONDS); - } - - private void makeHookStatic(Location armorLoc) { - armorLoc.setY(armorLoc.getBlockY() + 0.2); - if (hookedEntity != null && !hookedEntity.isDead()) - hookedEntity.remove(); - hookedEntity = armorLoc.getWorld().spawn(armorLoc, ArmorStand.class); - setTempEntity((ArmorStand) hookedEntity); - fishHook.setHookedEntity(hookedEntity); - } - - private void setTempEntity(ArmorStand entity) { - entity.setInvisible(true); - entity.setCollidable(false); - entity.setInvulnerable(true); - entity.setVisible(false); - entity.setCustomNameVisible(false); - entity.setSmall(true); - entity.setGravity(false); - entity.getPersistentDataContainer().set( - Objects.requireNonNull(NamespacedKey.fromString("lavafishing", CustomFishingPlugin.get())), - PersistentDataType.STRING, - "temp" - ); - } - - /** - * Checks if the fish hook is currently hooked. - * - * @return True if the fish hook is hooked, false otherwise. - */ - public boolean isFishHooked() { - return fishHooked; - } - - private void setWaitTime() { - if (CFConfig.overrideVanilla) { - double initialTime = ThreadLocalRandom.current().nextInt(CFConfig.waterMaxTime - CFConfig.waterMinTime + 1) + CFConfig.waterMinTime; - fishHook.setWaitTime(Math.max(1, (int) (initialTime * tempEffect.getWaitTimeMultiplier() + tempEffect.getWaitTime()))); - } else { - int maxWait = Math.max(2, (int) (fishHook.getMaxWaitTime() * tempEffect.getWaitTimeMultiplier() + tempEffect.getWaitTime())); - int minWait = Math.min(Math.max(1, (int) (fishHook.getMinWaitTime() * tempEffect.getWaitTimeMultiplier() + tempEffect.getWaitTime())), fishHook.getMaxWaitTime()); - fishHook.setWaitTime(ThreadLocalRandom.current().nextInt(minWait, maxWait + 1)); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/LavaEffectTask.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/LavaEffectTask.java deleted file mode 100644 index 22a06065..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/LavaEffectTask.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.fishing; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import org.bukkit.Location; -import org.bukkit.Particle; - -import java.util.concurrent.TimeUnit; - -/** - * A task responsible for creating a lava effect animation between two points. - */ -public class LavaEffectTask implements Runnable { - - private final Location startLoc; - private final Location endLoc; - private final Location controlLoc; - private int timer; - private final CancellableTask lavaTask; - private final HookCheckTimerTask hookCheckTimerTask; - - /** - * Constructs a new LavaEffectTask. - * - * @param hookCheckTimerTask The HookCheckTimerTask instance. - * @param location The starting location for the lava effect. - * @param delay The delay before starting the task. - */ - public LavaEffectTask(HookCheckTimerTask hookCheckTimerTask, Location location, int delay) { - this.hookCheckTimerTask = hookCheckTimerTask; - this.startLoc = location.clone().add(0,0.3,0); - this.endLoc = this.startLoc.clone().add((Math.random() * 16 - 8), startLoc.getY(), (Math.random() * 16 - 8)); - this.controlLoc = new Location( - startLoc.getWorld(), - (startLoc.getX() + endLoc.getX())/2 + Math.random() * 12 - 6, - startLoc.getY(), - (startLoc.getZ() + endLoc.getZ())/2 + Math.random() * 12 - 6 - ); - this.lavaTask = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(this, delay * 50L, 50, TimeUnit.MILLISECONDS); - } - - @Override - public void run() { - timer++; - if (timer > 60) { - lavaTask.cancel(); - CustomFishingPlugin.get().getScheduler().runTaskSync(hookCheckTimerTask::getHooked, startLoc); - } else { - double t = (double) timer / 60; - Location particleLoc = endLoc.clone().multiply(Math.pow((1 - t), 2)).add(controlLoc.clone().multiply(2 * t * (1 - t))).add(startLoc.clone().multiply(Math.pow(t, 2))); - particleLoc.setY(startLoc.getY()); - startLoc.getWorld().spawnParticle(Particle.FLAME, particleLoc,1,0,0,0,0); - } - } - - /** - * Cancels the lava effect task. - */ - public void cancel() { - if (lavaTask != null && !lavaTask.isCancelled()) - lavaTask.cancel(); - } - - /** - * Checks if the lava effect task is cancelled. - * - * @return True if the task is cancelled, false otherwise. - */ - public boolean isCancelled() { - return lavaTask.isCancelled(); - } -} 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 deleted file mode 100644 index 50037e9d..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java +++ /dev/null @@ -1,1209 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.game; - -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.manager.GameManager; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -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 net.momirealms.customfishing.util.ConfigUtils; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.*; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -@SuppressWarnings("DuplicatedCode") -public class GameManagerImpl implements GameManager { - - private final CustomFishingPlugin plugin; - private final HashMap gameCreatorMap; - private final HashMap> gameInstanceMap; - private final String EXPANSION_FOLDER = "expansions/minigame"; - - public GameManagerImpl(CustomFishingPlugin plugin) { - this.plugin = plugin; - this.gameCreatorMap = new HashMap<>(); - this.gameInstanceMap = new HashMap<>(); - this.registerInbuiltGames(); - } - - private void registerInbuiltGames() { - this.registerHoldGame(); - this.registerHoldV2Game(); - this.registerTensionGame(); - this.registerClickGame(); - this.registerDanceGame(); - this.registerAccurateClickGame(); - this.registerAccurateClickV2Game(); - this.registerAccurateClickV3Game(); - } - - public void load() { - this.loadExpansions(); - this.loadGamesFromPluginFolder(); - } - - public void unload() { - this.gameInstanceMap.clear(); - } - - public void disable() { - unload(); - 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)) - return false; - else - gameCreatorMap.put(type, gameFactory); - 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 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 ((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"); - if (!typeFolder.exists()) { - if (!typeFolder.mkdirs()) return; - plugin.saveResource("contents" + File.separator + "minigame" + File.separator + "default.yml", false); - } - fileDeque.push(typeFolder); - while (!fileDeque.isEmpty()) { - File file = fileDeque.pop(); - File[] files = file.listFiles(); - if (files == null) continue; - for (File subFile : files) { - if (subFile.isDirectory()) { - fileDeque.push(subFile); - } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { - loadSingleFile(subFile); - } - } - } - } - - /** - * 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) { - String[] split = str.split("~"); - basicGameBuilder.time(Integer.parseInt(split[0]), Integer.parseInt(split[1])); - } else if (time instanceof Integer integer) { - basicGameBuilder.time(integer); - } - Object difficulty = section.get("difficulty", "20~80"); - if (difficulty instanceof String str) { - String[] split = str.split("~"); - basicGameBuilder.difficulty(Integer.parseInt(split[0]), Integer.parseInt(split[1])); - } else if (difficulty instanceof Integer integer) { - basicGameBuilder.difficulty(integer); - } - gameInstanceMap.put(entry.getKey(), Pair.of(basicGameBuilder.build(), creator.setArgs(section))); - } - } - } - - private void registerAccurateClickGame() { - this.registerGameType("accurate_click", (section -> { - - Set chances = Objects.requireNonNull(section.getConfigurationSection("success-rate-sections")).getKeys(false); - var widthPerSection = section.getInt("arguments.width-per-section", 16); - var successRate = new double[chances.size()]; - for(int i = 0; i < chances.size(); i++) - successRate[i] = section.getDouble("success-rate-sections." + (i + 1)); - var totalWidth = chances.size() * widthPerSection - 1; - var pointerOffset = section.getInt("arguments.pointer-offset"); - var pointerWidth = section.getInt("arguments.pointer-width"); - var title = ConfigUtils.stringListArgs(section.get("title")); - var font = section.getString("subtitle.font"); - var barImage = section.getString("subtitle.bar"); - var pointerImage = section.getString("subtitle.pointer"); - - return (player, fishHook, settings) -> new AbstractGamingPlayer(player, fishHook, settings) { - - private int progress = -1; - private boolean face = true; - private final String sendTitle = title.get(ThreadLocalRandom.current().nextInt(title.size())); - - @Override - public void arrangeTask() { - var period = ((double) 10*(200-settings.getDifficulty()))/((double) (1+4*settings.getDifficulty())); - this.task = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer( - this, - 50, - (long) period, - TimeUnit.MILLISECONDS - ); - } - - @Override - public void onTick() { - if (face) progress++; - else progress--; - if (progress > totalWidth) { - face = !face; - progress = 2 * totalWidth - progress; - } else if (progress < 0) { - face = !face; - progress = -progress; - } - showUI(); - } - - public void showUI() { - String bar = FontUtils.surroundWithFont(barImage, font) - + OffsetUtils.getOffsetChars(pointerOffset + progress) - + FontUtils.surroundWithFont(pointerImage, font) - + OffsetUtils.getOffsetChars(totalWidth - progress - pointerWidth); - AdventureHelper.getInstance().sendTitle(player, sendTitle, bar,0,10,0); - } - - @Override - public boolean isSuccessful() { - if (isTimeOut) return false; - int last = progress / widthPerSection; - return (Math.random() < successRate[last]); - } - }; - })); - } - - private void registerHoldGame() { - this.registerGameType("hold", (section -> { - - var timeRequirements = section.getIntegerList("hold-time-requirements").stream().mapToInt(Integer::intValue).toArray(); - var judgementAreaImage = section.getString("subtitle.judgment-area"); - var pointerImage = section.getString("subtitle.pointer"); - var barEffectiveWidth = section.getInt("arguments.bar-effective-area-width"); - var judgementAreaOffset = section.getInt("arguments.judgment-area-offset"); - var judgementAreaWidth = section.getInt("arguments.judgment-area-width"); - var pointerIconWidth = section.getInt("arguments.pointer-icon-width"); - var punishment = section.getDouble("arguments.punishment"); - var progress = section.getStringList("progress").toArray(new String[0]); - var waterResistance = section.getDouble("arguments.water-resistance", 0.15); - var pullingStrength = section.getDouble("arguments.pulling-strength", 0.45); - var looseningLoss = section.getDouble("arguments.loosening-strength-loss", 0.3); - - var title = section.getString("title","{progress}"); - var font = section.getString("subtitle.font"); - var barImage = section.getString("subtitle.bar"); - var tip = section.getString("tip"); - - return (player, fishHook, settings) -> new AbstractGamingPlayer(player, fishHook, settings) { - private double hold_time; - private double judgement_position; - private double fish_position; - private double judgement_velocity; - private double fish_velocity; - private int timer; - private final int time_requirement = timeRequirements[ThreadLocalRandom.current().nextInt(timeRequirements.length)] * 1000; - private boolean played; - - @Override - public void arrangeTask() { - this.judgement_position = (double) (barEffectiveWidth - judgementAreaWidth) / 2; - this.task = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer( - this, - 50, - 33, - TimeUnit.MILLISECONDS - ); - } - - @Override - public void onTick() { - if (player.isSneaking()) addV(); - else reduceV(); - if (timer < 40 - (settings.getDifficulty() / 10)) { - timer++; - } else { - timer = 0; - if (Math.random() > ((double) 25 / (settings.getDifficulty() + 100))) { - burst(); - } - } - judgement_position += judgement_velocity; - fish_position += fish_velocity; - fraction(); - calibrate(); - if (fish_position >= judgement_position && fish_position + pointerIconWidth <= judgement_position + judgementAreaWidth) { - hold_time += 33; - } else { - hold_time -= punishment * 33; - } - if (hold_time >= time_requirement) { - setGameResult(true); - endGame(); - return; - } - hold_time = Math.max(0, Math.min(hold_time, time_requirement)); - showUI(); - } - - private void burst() { - if (Math.random() < (judgement_position / barEffectiveWidth)) { - judgement_velocity = -1 - 0.8 * Math.random() * ((double) settings.getDifficulty() / 15); - } else { - judgement_velocity = 1 + 0.8 * Math.random() * ((double) settings.getDifficulty() / 15); - } - } - - private void fraction() { - if (judgement_velocity > 0) { - judgement_velocity -= waterResistance; - if (judgement_velocity < 0) judgement_velocity = 0; - } else { - judgement_velocity += waterResistance; - if (judgement_velocity > 0) judgement_velocity = 0; - } - } - - private void reduceV() { - fish_velocity -= looseningLoss; - } - - private void addV() { - played = true; - fish_velocity += pullingStrength; - } - - private void calibrate() { - if (fish_position < 0) { - fish_position = 0; - fish_velocity = 0; - } - if (fish_position + pointerIconWidth > barEffectiveWidth) { - fish_position = barEffectiveWidth - pointerIconWidth; - fish_velocity = 0; - } - if (judgement_position < 0) { - judgement_position = 0; - judgement_velocity = 0; - } - if (judgement_position + judgementAreaWidth > barEffectiveWidth) { - judgement_position = barEffectiveWidth - judgementAreaWidth; - judgement_velocity = 0; - } - } - - public void showUI() { - String bar = FontUtils.surroundWithFont(barImage, font) - + OffsetUtils.getOffsetChars((int) (judgementAreaOffset + judgement_position)) - + FontUtils.surroundWithFont(judgementAreaImage, font) - + OffsetUtils.getOffsetChars((int) (barEffectiveWidth - judgement_position - judgementAreaWidth)) - + OffsetUtils.getOffsetChars((int) (-barEffectiveWidth - 1 + fish_position)) - + FontUtils.surroundWithFont(pointerImage, font) - + OffsetUtils.getOffsetChars((int) (barEffectiveWidth - fish_position - pointerIconWidth + 1)); - AdventureHelper.getInstance().sendTitle( - player, - tip != null && !played ? tip : title.replace("{progress}", progress[(int) ((hold_time / time_requirement) * progress.length)]), - bar, - 0, - 10, - 0 - ); - } - }; - })); - } - - private void registerTensionGame() { - this.registerGameType("tension", (section -> { - - var fishIconWidth = section.getInt("arguments.fish-icon-width"); - var fishImage = section.getString("subtitle.fish"); - var tension = section.getStringList("tension").toArray(new String[0]); - var strugglingFishImage = section.getStringList("subtitle.struggling-fish").toArray(new String[0]); - var barEffectiveWidth = section.getInt("arguments.bar-effective-area-width"); - var fishOffset = section.getInt("arguments.fish-offset"); - var fishStartPosition = section.getInt("arguments.fish-start-position"); - var successPosition = section.getInt("arguments.success-position"); - var ultimateTension = section.getDouble("arguments.ultimate-tension", 50); - var normalIncrease = section.getDouble("arguments.normal-pull-tension-increase", 1); - var strugglingIncrease = section.getDouble("arguments.struggling-tension-increase", 2); - var tensionLoss = section.getDouble("arguments.loosening-tension-loss", 2); - - var title = section.getString("title","{progress}"); - var font = section.getString("subtitle.font"); - var barImage = section.getString("subtitle.bar"); - var tip = section.getString("tip"); - - return (player, fishHook, settings) -> new AbstractGamingPlayer(player, fishHook, settings) { - - private int fish_position = fishStartPosition; - private double strain; - private int struggling_time; - private boolean played; - - @Override - public void arrangeTask() { - this.task = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(this, 50, 40, TimeUnit.MILLISECONDS); - } - - @Override - public void onTick() { - if (struggling_time <= 0) { - if (Math.random() < ((double) settings.getDifficulty() / 4000)) { - struggling_time = (int) (10 + Math.random() * (settings.getDifficulty() / 4)); - } - } else { - struggling_time--; - } - if (player.isSneaking()) pull(); - else loosen(); - if (fish_position < successPosition - fishIconWidth - 1) { - setGameResult(true); - endGame(); - return; - } - if (fish_position + fishIconWidth > barEffectiveWidth || strain >= ultimateTension) { - setGameResult(false); - endGame(); - return; - } - showUI(); - } - - public void pull() { - played = true; - if (struggling_time > 0) { - strain += (strugglingIncrease + ((double) settings.getDifficulty() / 50)); - fish_position -= 1; - } else { - strain += normalIncrease; - fish_position -= 2; - } - } - - public void loosen() { - fish_position++; - strain -= tensionLoss; - } - - public void showUI() { - String bar = FontUtils.surroundWithFont(barImage, font) - + OffsetUtils.getOffsetChars(fishOffset + fish_position) - + FontUtils.surroundWithFont((struggling_time > 0 ? strugglingFishImage[struggling_time % strugglingFishImage.length] : fishImage), font) - + OffsetUtils.getOffsetChars(barEffectiveWidth - fish_position - fishIconWidth); - strain = Math.max(0, Math.min(strain, ultimateTension)); - AdventureHelper.getInstance().sendTitle( - player, - tip != null && !played ? tip : title.replace("{tension}", tension[(int) ((strain / ultimateTension) * tension.length)]), - bar, - 0, - 10, - 0 - ); - } - }; - })); - } - - private void registerDanceGame() { - this.registerGameType("dance", (section -> { - - var subtitle = section.getString("subtitle", "Dance to win. Time left {time}s"); - var leftNot = section.getString("title.left-button"); - var leftCorrect = section.getString("title.left-button-correct"); - var leftWrong = section.getString("title.left-button-wrong"); - var leftCurrent = section.getString("title.left-button-current"); - var rightNot = section.getString("title.right-button"); - var rightCorrect = section.getString("title.right-button-correct"); - var rightWrong = section.getString("title.right-button-wrong"); - var rightCurrent = section.getString("title.right-button-current"); - - var upNot = section.getString("title.up-button"); - var upCorrect = section.getString("title.up-button-correct"); - var upWrong = section.getString("title.up-button-wrong"); - var upCurrent = section.getString("title.up-button-current"); - var downNot = section.getString("title.down-button"); - var downCorrect = section.getString("title.down-button-correct"); - var downWrong = section.getString("title.down-button-wrong"); - var downCurrent = section.getString("title.down-button-current"); - - var maxShown = section.getInt("title.display-amount", 7); - var tip = section.getString("tip"); - var easy = section.getBoolean("easy", false); - - var correctSound = section.getString("sound.correct", "minecraft:block.amethyst_block.hit"); - var wrongSound = section.getString("sound.wrong", "minecraft:block.anvil.land"); - - return (player, fishHook, settings) -> new AbstractGamingPlayer(player, fishHook, settings) { - - private int clickedTimes; - private int requiredTimes; - private boolean preventFirst = true; - // 0 = left / 1 = right / 2 = up / 3 = down - private int[] order; - boolean fail = false; - - @Override - public void arrangeTask() { - requiredTimes = settings.getDifficulty() / 4; - order = new int[requiredTimes]; - for (int i = 0; i < requiredTimes; i++) { - order[i] = ThreadLocalRandom.current().nextInt(0, easy ? 2 : 4); - } - this.task = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(this, 50, 50, TimeUnit.MILLISECONDS); - } - - @Override - public void onTick() { - showUI(); - if (tip != null) { - AdventureHelper.getInstance().sendActionbar(player, tip); - } - } - - @Override - public boolean onRightClick() { - preventFirst = true; - if (order[clickedTimes] != 1) { - setGameResult(false); - fail = true; - showUI(); - AdventureHelper.getInstance().sendSound( - player, - Sound.Source.PLAYER, - Key.key(wrongSound), - 1, - 1 - ); - endGame(); - return true; - } - - AdventureHelper.getInstance().sendSound( - player, - Sound.Source.PLAYER, - Key.key(correctSound), - 1, - 1 - ); - clickedTimes++; - if (clickedTimes >= requiredTimes) { - setGameResult(true); - showUI(); - endGame(); - } - return true; - } - - @Override - public boolean onJump() { - if (order[clickedTimes] != 2) { - setGameResult(false); - fail = true; - showUI(); - AdventureHelper.getInstance().sendSound( - player, - Sound.Source.PLAYER, - Key.key(wrongSound), - 1, - 1 - ); - endGame(); - return false; - } - - AdventureHelper.getInstance().sendSound( - player, - Sound.Source.PLAYER, - Key.key(correctSound), - 1, - 1 - ); - clickedTimes++; - if (clickedTimes >= requiredTimes) { - setGameResult(true); - showUI(); - endGame(); - } - return false; - } - - @Override - public boolean onSneak() { - if (order[clickedTimes] != 3) { - setGameResult(false); - fail = true; - showUI(); - AdventureHelper.getInstance().sendSound( - player, - Sound.Source.PLAYER, - Key.key(wrongSound), - 1, - 1 - ); - endGame(); - return false; - } - - AdventureHelper.getInstance().sendSound( - player, - Sound.Source.PLAYER, - Key.key(correctSound), - 1, - 1 - ); - clickedTimes++; - if (clickedTimes >= requiredTimes) { - setGameResult(true); - showUI(); - endGame(); - } - return false; - } - - @Override - public boolean onLeftClick() { - if (preventFirst) { - preventFirst = false; - return false; - } - - if (order[clickedTimes] != 0) { - setGameResult(false); - fail = true; - showUI(); - AdventureHelper.getInstance().sendSound( - player, - Sound.Source.PLAYER, - Key.key(wrongSound), - 1, - 1 - ); - endGame(); - return true; - } - - AdventureHelper.getInstance().sendSound( - player, - Sound.Source.PLAYER, - Key.key(correctSound), - 1, - 1 - ); - clickedTimes++; - if (clickedTimes >= requiredTimes) { - setGameResult(true); - showUI(); - endGame(); - } - return false; - } - - public void showUI() { - try { - if (requiredTimes <= maxShown) { - StringBuilder sb = new StringBuilder(); - for (int x = 0; x < requiredTimes; x++) { - if (x < clickedTimes) { - switch (order[x]) { - case 0 -> sb.append(leftCorrect); - case 1 -> sb.append(rightCorrect); - case 2 -> sb.append(upCorrect); - case 3 -> sb.append(downCorrect); - } - } else if (clickedTimes == x) { - switch (order[x]) { - case 0 -> sb.append(fail ? leftWrong : leftCurrent); - case 1 -> sb.append(fail ? rightWrong : rightCurrent); - case 2 -> sb.append(fail ? upWrong : upCurrent); - case 3 -> sb.append(fail ? downWrong : downCurrent); - } - } else { - switch (order[x]) { - case 0 -> sb.append(leftNot); - case 1 -> sb.append(rightNot); - case 2 -> sb.append(upNot); - case 3 -> sb.append(downNot); - } - } - } - AdventureHelper.getInstance().sendTitle( - player, - sb.toString(), - subtitle.replace("{time}", String.format("%.1f", ((double) deadline - System.currentTimeMillis())/1000)), - 0, - 10, - 0 - ); - } else { - int half = (maxShown - 1) / 2; - int low = clickedTimes - half; - int high = clickedTimes + half; - if (low < 0) { - high += (-low); - low = 0; - } else if (high >= requiredTimes) { - low -= (high - requiredTimes + 1); - high = requiredTimes - 1; - } - StringBuilder sb = new StringBuilder(); - for (int x = low; x < high + 1; x++) { - if (x < clickedTimes) { - switch (order[x]) { - case 0 -> sb.append(leftCorrect); - case 1 -> sb.append(rightCorrect); - case 2 -> sb.append(upCorrect); - case 3 -> sb.append(downCorrect); - } - } else if (clickedTimes == x) { - switch (order[x]) { - case 0 -> sb.append(fail ? leftWrong : leftCurrent); - case 1 -> sb.append(fail ? rightWrong : rightCurrent); - case 2 -> sb.append(fail ? upWrong : upCurrent); - case 3 -> sb.append(fail ? downWrong : downCurrent); - } - } else { - switch (order[x]) { - case 0 -> sb.append(leftNot); - case 1 -> sb.append(rightNot); - case 2 -> sb.append(upNot); - case 3 -> sb.append(downNot); - } - } - } - AdventureHelper.getInstance().sendTitle( - player, - sb.toString(), - subtitle.replace("{time}", String.format("%.1f", ((double) deadline - System.currentTimeMillis())/1000)), - 0, - 10, - 0 - ); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - }; - })); - } - - private void registerClickGame() { - this.registerGameType("click", (section -> { - - var title = section.getString("title","{click}"); - var subtitle = section.getString("subtitle", "Click {clicks} times to win. Time left {time}s"); - var left = section.getBoolean("left-click", true); - - return (player, fishHook, settings) -> new AbstractGamingPlayer(player, fishHook, settings) { - - private int clickedTimes; - private final int requiredTimes = settings.getDifficulty(); - private boolean preventFirst = true; - - @Override - public void arrangeTask() { - this.task = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(this, 50, 50, TimeUnit.MILLISECONDS); - } - - @Override - public void onTick() { - showUI(); - } - - @Override - public boolean onRightClick() { - if (left) { - setGameResult(false); - endGame(); - return true; - } - clickedTimes++; - if (clickedTimes >= requiredTimes) { - showUI(); - setGameResult(true); - endGame(); - } - return true; - } - - @Override - public boolean onLeftClick() { - if (!left) { - return false; - } - if (preventFirst) { - preventFirst = false; - return false; - } - clickedTimes++; - if (clickedTimes >= requiredTimes) { - showUI(); - setGameResult(true); - endGame(); - } - return false; - } - - public void showUI() { - AdventureHelper.getInstance().sendTitle( - player, - title.replace("{click}", String.valueOf(clickedTimes)), - subtitle.replace("{clicks}", String.valueOf(requiredTimes)).replace("{time}", String.format("%.1f", ((double) deadline - System.currentTimeMillis())/1000)), - 0, - 10, - 0 - ); - } - }; - })); - } - - private void registerAccurateClickV2Game() { - - this.registerGameType("accurate_click_v2", (section -> { - - var barWidth = ConfigUtils.getIntegerPair(section.getString("title.total-width", "15~20")); - var barSuccess = ConfigUtils.getIntegerPair(section.getString("title.success-width","3~4")); - var barBody = section.getString("title.body",""); - var barPointer = section.getString("title.pointer", ""); - var barTarget = section.getString("title.target",""); - - var subtitle = section.getString("subtitle", "Reel in at the most critical moment"); - - return (player, fishHook, settings) -> new AbstractGamingPlayer(player, fishHook, settings) { - - private final int totalWidth = ThreadLocalRandom.current().nextInt(barWidth.right() - barWidth.left() + 1) + barWidth.left(); - private final int successWidth = ThreadLocalRandom.current().nextInt(barSuccess.right() - barSuccess.left() + 1) + barSuccess.left(); - private final int successPosition = ThreadLocalRandom.current().nextInt((totalWidth - successWidth + 1)) + 1; - private int currentIndex = 0; - private int timer = 0; - private boolean face = true; - - @Override - public void arrangeTask() { - this.task = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(this, 50, 50, TimeUnit.MILLISECONDS); - } - - @Override - public void onTick() { - timer++; - if (timer % (21 - settings.getDifficulty() / 5) == 0) { - movePointer(); - } - showUI(); - } - - private void movePointer() { - if (face) { - currentIndex++; - if (currentIndex >= totalWidth - 1) { - face = false; - } - } else { - currentIndex--; - if (currentIndex <= 0) { - face = true; - } - } - } - - public void showUI() { - StringBuilder stringBuilder = new StringBuilder(); - for (int i = 1; i <= totalWidth; i++) { - if (i == currentIndex + 1) { - stringBuilder.append(barPointer); - continue; - } - if (i >= successPosition && i <= successPosition + successWidth - 1) { - stringBuilder.append(barTarget); - continue; - } - stringBuilder.append(barBody); - } - - AdventureHelper.getInstance().sendTitle( - player, - stringBuilder.toString(), - subtitle, - 0, - 10, - 0 - ); - } - - @Override - public boolean isSuccessful() { - return currentIndex + 1 <= successPosition + successWidth - 1 && currentIndex + 1 >= successPosition; - } - }; - })); - } - - private void registerAccurateClickV3Game() { - - this.registerGameType("accurate_click_v3", (section -> { - - var font = section.getString("subtitle.font"); - var pointerImage = section.getString("subtitle.pointer"); - var barImage = section.getString("subtitle.bar"); - var judgementAreaImage = section.getString("subtitle.judgment-area"); - var titles = ConfigUtils.stringListArgs(section.get("title")); - - var barEffectiveWidth = section.getInt("arguments.bar-effective-area-width"); - var judgementAreaWidth = section.getInt("arguments.judgment-area-width"); - var judgementAreaOffset = section.getInt("arguments.judgment-area-offset"); - var pointerIconWidth = section.getInt("arguments.pointer-icon-width"); - var pointerOffset = section.getInt("arguments.pointer-offset"); - - return (player, fishHook, settings) -> new AbstractGamingPlayer(player, fishHook, settings) { - - private int progress = -1; - private boolean face = true; - private final int judgement_position = ThreadLocalRandom.current().nextInt(barEffectiveWidth - judgementAreaWidth + 1); - private final String title = titles.get(ThreadLocalRandom.current().nextInt(titles.size())); - - @Override - public void arrangeTask() { - var period = ((double) 10*(200-settings.getDifficulty()))/((double) (1+4*settings.getDifficulty())); - this.task = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer( - this, - 50, - (long) period, - TimeUnit.MILLISECONDS - ); - } - - @Override - public void onTick() { - if (face) { - progress++; - if (progress >= barEffectiveWidth - 1) { - face = false; - } - } else { - progress--; - if (progress <= 0) { - face = true; - } - } - showUI(); - } - - public void showUI() { - String bar = FontUtils.surroundWithFont(barImage, font) - + OffsetUtils.getOffsetChars(judgementAreaOffset + judgement_position) - + FontUtils.surroundWithFont(judgementAreaImage, font) - + OffsetUtils.getOffsetChars(barEffectiveWidth - judgement_position - judgementAreaWidth) - + OffsetUtils.getOffsetChars(progress + pointerOffset) - + FontUtils.surroundWithFont(pointerImage, font) - + OffsetUtils.getOffsetChars(barEffectiveWidth - progress - pointerIconWidth + 1); - AdventureHelper.getInstance().sendTitle( - player, - title, - bar, - 0, - 10, - 0 - ); - } - - @Override - public boolean isSuccessful() { - return progress < judgement_position + judgementAreaWidth && progress >= judgement_position; - } - }; - })); - } - - private void registerHoldV2Game() { - this.registerGameType("hold_v2", (section -> { - - var timeRequirements = section.getIntegerList("hold-time-requirements").stream().mapToInt(Integer::intValue).toArray(); - var judgementAreaImage = section.getString("subtitle.judgment-area"); - var pointerImage = section.getString("subtitle.pointer"); - var barEffectiveWidth = section.getInt("arguments.bar-effective-area-width"); - var judgementAreaOffset = section.getInt("arguments.judgment-area-offset"); - var judgementAreaWidth = section.getInt("arguments.judgment-area-width"); - var pointerIconWidth = section.getInt("arguments.pointer-icon-width"); - var punishment = section.getDouble("arguments.punishment"); - var progress = section.getStringList("progress").toArray(new String[0]); - var waterResistance = section.getDouble("arguments.water-resistance", 0.15); - var pullingStrength = section.getDouble("arguments.pulling-strength", 3); - var looseningLoss = section.getDouble("arguments.loosening-strength-loss", 0.5); - - var title = section.getString("title", "{progress}"); - var font = section.getString("subtitle.font"); - var barImage = section.getString("subtitle.bar"); - var tip = section.getString("tip"); - - var left = section.getBoolean("left-click", true); - - return (player, fishHook, settings) -> new AbstractGamingPlayer(player, fishHook, settings) { - private double hold_time; - private double judgement_position; - private double fish_position; - private double judgement_velocity; - private double fish_velocity; - private int timer; - private final int time_requirement = timeRequirements[ThreadLocalRandom.current().nextInt(timeRequirements.length)] * 1000; - private boolean played; - private boolean preventFirst = true; - - @Override - public void arrangeTask() { - this.judgement_position = (double) (barEffectiveWidth - judgementAreaWidth) / 2; - this.task = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer( - this, - 50, - 33, - TimeUnit.MILLISECONDS - ); - } - - @Override - public void onTick() { - if (timer < 40 - (settings.getDifficulty() / 10)) { - timer++; - } else { - timer = 0; - if (Math.random() > ((double) 25 / (settings.getDifficulty() + 100))) { - burst(); - } - } - judgement_position += judgement_velocity; - fish_position += fish_velocity; - fraction(); - calibrate(); - if (fish_position >= judgement_position && fish_position + pointerIconWidth <= judgement_position + judgementAreaWidth) { - hold_time += 33; - } else { - hold_time -= punishment * 33; - } - if (hold_time >= time_requirement) { - setGameResult(true); - endGame(); - return; - } - hold_time = Math.max(0, Math.min(hold_time, time_requirement)); - showUI(); - } - - private void burst() { - if (Math.random() < (judgement_position / barEffectiveWidth)) { - judgement_velocity = -1 - 0.8 * Math.random() * ((double) settings.getDifficulty() / 15); - } else { - judgement_velocity = 1 + 0.8 * Math.random() * ((double) settings.getDifficulty() / 15); - } - } - - private void fraction() { - if (judgement_velocity > 0) { - judgement_velocity -= waterResistance; - if (judgement_velocity < 0) judgement_velocity = 0; - } else { - judgement_velocity += waterResistance; - if (judgement_velocity > 0) judgement_velocity = 0; - } - fish_velocity -= looseningLoss; - if (fish_velocity < -10 * looseningLoss) { - fish_velocity = -10 * looseningLoss; - } - } - - private void calibrate() { - if (fish_position < 0) { - fish_position = 0; - fish_velocity = 0; - } - if (fish_position + pointerIconWidth > barEffectiveWidth) { - fish_position = barEffectiveWidth - pointerIconWidth; - fish_velocity = 0; - } - if (judgement_position < 0) { - judgement_position = 0; - judgement_velocity = 0; - } - if (judgement_position + judgementAreaWidth > barEffectiveWidth) { - judgement_position = barEffectiveWidth - judgementAreaWidth; - judgement_velocity = 0; - } - } - - @Override - public boolean onRightClick() { - if (left) { - setGameResult(false); - endGame(); - return true; - } - played = true; - fish_velocity = pullingStrength; - return true; - } - - @Override - public boolean onLeftClick() { - if (preventFirst) { - preventFirst = false; - return false; - } - if (left) { - played = true; - fish_velocity = pullingStrength; - } - return false; - } - - public void showUI() { - String bar = FontUtils.surroundWithFont(barImage, font) - + OffsetUtils.getOffsetChars((int) (judgementAreaOffset + judgement_position)) - + FontUtils.surroundWithFont(judgementAreaImage, font) - + OffsetUtils.getOffsetChars((int) (barEffectiveWidth - judgement_position - judgementAreaWidth)) - + OffsetUtils.getOffsetChars((int) (-barEffectiveWidth - 1 + fish_position)) - + FontUtils.surroundWithFont(pointerImage, font) - + OffsetUtils.getOffsetChars((int) (barEffectiveWidth - fish_position - pointerIconWidth + 1)); - AdventureHelper.getInstance().sendTitle( - player, - tip != null && !played ? tip : title.replace("{progress}", progress[(int) ((hold_time / time_requirement) * progress.length)]), - bar, - 0, - 10, - 0 - ); - } - }; - })); - } - - /** - * Loads minigame expansions from the expansion folder. - */ - @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, GameExpansion.class); - classes.add(expansionClass); - } catch (IOException | ClassNotFoundException e) { - LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); - } - } - } - try { - for (Class expansionClass : classes) { - GameExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); - unregisterGameType(expansion.getGameType()); - registerGameType(expansion.getGameType(), expansion.getGameFactory()); - LogUtils.info("Loaded minigame expansion: " + expansion.getGameType() + "[" + 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/hook/HookManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/hook/HookManagerImpl.java deleted file mode 100644 index 0c6a830a..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/hook/HookManagerImpl.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.hook; - -import de.tr7zw.changeme.nbtapi.NBTCompound; -import de.tr7zw.changeme.nbtapi.NBTItem; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.manager.HookManager; -import net.momirealms.customfishing.api.manager.RequirementManager; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.effect.EffectCarrier; -import net.momirealms.customfishing.api.mechanic.hook.HookSetting; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.mechanic.item.ItemManagerImpl; -import net.momirealms.customfishing.util.ItemUtils; -import org.bukkit.Bukkit; -import org.bukkit.GameMode; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.util.*; - -public class HookManagerImpl implements Listener, HookManager { - - private final CustomFishingPlugin plugin; - private final HashMap hookSettingMap; - - public HookManagerImpl(CustomFishingPlugin plugin) { - this.plugin = plugin; - this.hookSettingMap = new HashMap<>(); - } - - public void load() { - Bukkit.getPluginManager().registerEvents(this, plugin); - loadConfig(); - } - - public void unload() { - HandlerList.unregisterAll(this); - hookSettingMap.clear(); - } - - public void disable() { - unload(); - } - - /** - * Loads configuration files for the specified types. - */ - @SuppressWarnings("DuplicatedCode") - private void loadConfig() { - Deque fileDeque = new ArrayDeque<>(); - for (String type : List.of("hook")) { - File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type); - if (!typeFolder.exists()) { - if (!typeFolder.mkdirs()) return; - plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false); - } - fileDeque.push(typeFolder); - while (!fileDeque.isEmpty()) { - File file = fileDeque.pop(); - File[] files = file.listFiles(); - if (files == null) continue; - for (File subFile : files) { - if (subFile.isDirectory()) { - fileDeque.push(subFile); - } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { - this.loadSingleFile(subFile); - } - } - } - } - } - - /** - * 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()) { - if (entry.getValue() instanceof ConfigurationSection section) { - if (!section.contains("max-durability")) { - LogUtils.warn("Please set max-durability to hook: " + entry.getKey()); - continue; - } - var setting = new HookSetting.Builder(entry.getKey()) - .durability(section.getInt("max-durability", 16)) - .lore(section.getStringList("lore-on-rod").stream().map(it -> "" + it).toList()) - .build(); - hookSettingMap.put(entry.getKey(), setting); - } - } - } - - /** - * 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; - - var curDurability = ItemUtils.getCustomDurability(hook); - if (curDurability.left() == 0) - 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", curDurability.right()); - - 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; - final Player player = (Player) event.getWhoClicked(); - if (event.getClickedInventory() != player.getInventory()) - return; - ItemStack clicked = event.getCurrentItem(); - if (clicked == null || clicked.getType() != Material.FISHING_ROD) - return; - if (player.getGameMode() != GameMode.SURVIVAL) - return; - if (plugin.getFishingManager().hasPlayerCastHook(player.getUniqueId())) - return; - - ItemStack cursor = event.getCursor(); - if (cursor.getType() == Material.AIR) { - if (event.getClick() == ClickType.RIGHT) { - NBTItem nbtItem = new NBTItem(clicked); - NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); - if (cfCompound == null) - return; - if (cfCompound.hasTag("hook_id")) { - event.setCancelled(true); - ItemStack hook = cfCompound.getItemStack("hook_item"); - ItemUtils.setDurability(hook, cfCompound.getInteger("hook_dur"), true); - cfCompound.removeKey("hook_id"); - cfCompound.removeKey("hook_item"); - cfCompound.removeKey("hook_dur"); - event.setCursor(hook); - ItemUtils.updateNBTItemLore(nbtItem); - clicked.setItemMeta(nbtItem.getItem().getItemMeta()); - } - } - return; - } - - String hookID = plugin.getItemManager().getAnyPluginItemID(cursor); - HookSetting setting = getHookSetting(hookID); - if (setting == null) - return; - - var cursorDurability = ItemUtils.getCustomDurability(cursor); - if (cursorDurability.left() == 0) { - if (plugin.getItemManager().getBuildableItem("hook", hookID) instanceof ItemManagerImpl.CFBuilder cfBuilder) { - ItemStack itemStack = cfBuilder.build(player, new HashMap<>()); - var pair = ItemUtils.getCustomDurability(itemStack); - cursorDurability = pair; - NBTItem nbtItem = new NBTItem(cursor); - NBTCompound compound = nbtItem.getOrCreateCompound("CustomFishing"); - compound.setInteger("max_dur", pair.left()); - compound.setInteger("cur_dur", pair.right()); - compound.setString("type", "hook"); - compound.setString("id", hookID); - cursor.setItemMeta(nbtItem.getItem().getItemMeta()); - } else { - return; - } - } - - Condition condition = new Condition(player, new HashMap<>()); - condition.insertArg("{rod}", plugin.getItemManager().getAnyPluginItemID(clicked)); - EffectCarrier effectCarrier = plugin.getEffectManager().getEffectCarrier("hook", hookID); - if (effectCarrier != null) { - if (!RequirementManager.isRequirementMet(condition, effectCarrier.getRequirements())) { - return; - } - } - - event.setCancelled(true); - - NBTItem rodNBTItem = new NBTItem(clicked); - NBTCompound cfCompound = rodNBTItem.getOrCreateCompound("CustomFishing"); - String previousHookID = cfCompound.getString("hook_id"); - - ItemStack clonedHook = cursor.clone(); - clonedHook.setAmount(1); - cursor.setAmount(cursor.getAmount() - 1); - - if (previousHookID != null && !previousHookID.isEmpty()) { - int previousHookDurability = cfCompound.getInteger("hook_dur"); - ItemStack previousItemStack = cfCompound.getItemStack("hook_item"); - ItemUtils.setDurability(previousItemStack, previousHookDurability, true); - if (cursor.getAmount() == 0) { - event.setCursor(previousItemStack); - } else { - ItemUtils.giveItem(player, previousItemStack, 1); - } - } - - cfCompound.setString("hook_id", hookID); - cfCompound.setItemStack("hook_item", clonedHook); - cfCompound.setInteger("hook_dur", cursorDurability.right()); - - ItemUtils.updateNBTItemLore(rodNBTItem); - clicked.setItemMeta(rodNBTItem.getItem().getItemMeta()); - } -} 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 deleted file mode 100644 index 696e3a6f..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java +++ /dev/null @@ -1,1124 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.item; - -import de.tr7zw.changeme.nbtapi.*; -import net.momirealms.customfishing.adventure.AdventureHelper; -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.common.Tuple; -import net.momirealms.customfishing.api.event.FishingBagPreCollectEvent; -import net.momirealms.customfishing.api.event.FishingLootPreSpawnEvent; -import net.momirealms.customfishing.api.event.FishingLootSpawnEvent; -import net.momirealms.customfishing.api.manager.ActionManager; -import net.momirealms.customfishing.api.manager.ItemManager; -import net.momirealms.customfishing.api.manager.RequirementManager; -import net.momirealms.customfishing.api.mechanic.GlobalSettings; -import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.effect.EffectCarrier; -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 net.momirealms.customfishing.api.mechanic.misc.Value; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.api.util.WeightUtils; -import net.momirealms.customfishing.compatibility.item.CustomFishingItemImpl; -import net.momirealms.customfishing.compatibility.item.VanillaItemImpl; -import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; -import net.momirealms.customfishing.setting.CFConfig; -import net.momirealms.customfishing.util.ConfigUtils; -import net.momirealms.customfishing.util.ItemUtils; -import net.momirealms.customfishing.util.LocationUtils; -import net.momirealms.customfishing.util.NBTUtils; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.block.Block; -import org.bukkit.block.Skull; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.Item; -import org.bukkit.entity.Player; -import org.bukkit.event.Event; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.*; -import org.bukkit.event.entity.EntityExplodeEvent; -import org.bukkit.event.inventory.InventoryPickupItemEvent; -import org.bukkit.event.inventory.PrepareAnvilEvent; -import org.bukkit.event.player.PlayerAttemptPickupItemEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerItemConsumeEvent; -import org.bukkit.event.player.PlayerItemMendEvent; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.ItemFlag; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.Damageable; -import org.bukkit.persistence.PersistentDataContainer; -import org.bukkit.persistence.PersistentDataType; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.util.*; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -public class ItemManagerImpl implements ItemManager, Listener { - - private static ItemManager instance; - private final CustomFishingPlugin plugin; - private final HashMap buildableItemMap; - private final HashMap itemLibraryMap; - private ItemLibrary[] itemDetectionArray; - private NamespacedKey blockKey; - - public ItemManagerImpl(CustomFishingPlugin plugin) { - instance = this; - this.plugin = plugin; - this.itemLibraryMap = new LinkedHashMap<>(); - this.buildableItemMap = new HashMap<>(); - this.blockKey = NamespacedKey.fromString("block", plugin); - this.registerItemLibrary(new CustomFishingItemImpl()); - this.registerItemLibrary(new VanillaItemImpl()); - } - - public void load() { - this.loadItemsFromPluginFolder(); - Bukkit.getPluginManager().registerEvents(this, plugin); - this.resetItemDetectionOrder(); - } - - public void unload() { - HandlerList.unregisterAll(this); - HashMap tempMap = new HashMap<>(this.buildableItemMap); - this.buildableItemMap.clear(); - for (Map.Entry entry : tempMap.entrySet()) { - if (entry.getValue().persist()) { - tempMap.put(entry.getKey(), entry.getValue()); - } - } - } - - private void resetItemDetectionOrder() { - ArrayList list = new ArrayList<>(); - for (String plugin : CFConfig.itemDetectOrder) { - ItemLibrary library = itemLibraryMap.get(plugin); - if (library != null) { - list.add(library); - } - } - this.itemDetectionArray = list.toArray(new ItemLibrary[0]); - } - - public Collection getItemLibraries() { - return itemLibraryMap.keySet(); - } - - /** - * Get a set of all item keys in the CustomFishing plugin. - * - * @return A set of item keys. - */ - @Override - public Set getAllItemsKey() { - return buildableItemMap.keySet(); - } - - public void disable() { - HandlerList.unregisterAll(this); - this.buildableItemMap.clear(); - this.itemLibraryMap.clear(); - } - - /** - * Loads items from the plugin folder. - * This method scans various item types (item, bait, rod, util, hook) in the plugin's content folder and loads their configurations. - */ - @SuppressWarnings("DuplicatedCode") - public void loadItemsFromPluginFolder() { - Deque fileDeque = new ArrayDeque<>(); - for (String type : List.of("item", "bait", "rod", "util", "hook")) { - File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type); - if (!typeFolder.exists()) { - if (!typeFolder.mkdirs()) return; - plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false); - } - fileDeque.push(typeFolder); - while (!fileDeque.isEmpty()) { - File file = fileDeque.pop(); - File[] files = file.listFiles(); - if (files == null) continue; - for (File subFile : files) { - if (subFile.isDirectory()) { - fileDeque.push(subFile); - } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { - this.loadSingleFile(subFile, type); - } - } - } - } - } - - /** - * Loads a single item configuration file. - * - * @param file The YAML configuration file to load. - * @param namespace The namespace of the item type (item, bait, rod, util, hook). - */ - 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); - if (buildableItemMap.containsKey(key)) { - LogUtils.severe("Duplicated item key found: " + key + "."); - } else { - buildableItemMap.put(key, getItemBuilder(section, namespace, value)); - } - } - } - } - - /** - * 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)); - if (buildableItem == null) return null; - 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 getAnyPluginItemID(ItemStack itemStack) { - if (itemStack == null || itemStack.getType() == Material.AIR) - return "AIR"; - for (ItemLibrary library : itemDetectionArray) { - String id = library.getItemID(itemStack); - if (id != null) { - return id; - } - } - // should not reach this because vanilla library would always work - 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 buildAnyPluginItemByID(Player player, String id) { - if (id.contains(":")) { - String[] split = id.split(":", 2); - return itemLibraryMap.get(split[0]).buildItem(player, split[1]); - } else { - try { - return new ItemStack(Material.valueOf(id.toUpperCase(Locale.ENGLISH))); - } catch (IllegalArgumentException e) { - return new ItemStack(Material.COD); - } - } - } - - /** - * 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") && !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 getCustomFishingItemID(ItemStack itemStack) { - if (itemStack == null || itemStack.getType() == Material.AIR) return null; - NBTItem nbtItem = new NBTItem(itemStack); - NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); - if (cfCompound == null) return null; - 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) { - if (section == null) return null; - String material = section.getString("material", type.equals("rod") ? "FISHING_ROD" : "PAPER"); - CFBuilder itemCFBuilder; - if (material.contains(":")) { - String[] split = material.split(":", 2); - itemCFBuilder = CFBuilder.of(split[0], split[1]); - } else { - itemCFBuilder = CFBuilder.of("vanilla", material.toUpperCase(Locale.ENGLISH)); - } - itemCFBuilder - .stackable(section.getBoolean("stackable", true)) - .size(ConfigUtils.getFloatPair(section.getString("size"))) - .price((float) section.getDouble("price.base"), (float) section.getDouble("price.bonus")) - .customModelData(section.getInt("custom-model-data")) - .nbt(section.getConfigurationSection("nbt")) - .maxDurability(section.getInt("max-durability")) - .itemFlag(section.getStringList("item-flags").stream().map(flag -> ItemFlag.valueOf(flag.toUpperCase())).toList()) - .enchantment(ConfigUtils.getEnchantmentPair(section.getConfigurationSection("enchantments")), false) - .enchantment(ConfigUtils.getEnchantmentPair(section.getConfigurationSection("stored-enchantments")), true) - .enchantmentPool(ConfigUtils.getEnchantAmountPair(section.getConfigurationSection("enchantment-pool.amount")), ConfigUtils.getEnchantPoolPair(section.getConfigurationSection("enchantment-pool.pool")), false) - .enchantmentPool(ConfigUtils.getEnchantAmountPair(section.getConfigurationSection("stored-enchantment-pool.amount")), ConfigUtils.getEnchantPoolPair(section.getConfigurationSection("stored-enchantment-pool.pool")), true) - .randomEnchantments(ConfigUtils.getEnchantmentTuple(section.getConfigurationSection("random-enchantments")), false) - .randomEnchantments(ConfigUtils.getEnchantmentTuple(section.getConfigurationSection("random-stored-enchantments")), true) - .tag(section.getBoolean("tag", true), type, id) - .randomDamage(section.getBoolean("random-durability", false)) - .unbreakable(section.getBoolean("unbreakable", false)) - .preventGrabbing(section.getBoolean("prevent-grabbing", true)) - .placeable(section.getBoolean("placeable", false)) - .head(section.getString("head64")) - .name(section.getString("display.name")) - .lore(section.getStringList("display.lore")); - if (section.get("amount") instanceof String s) { - Pair pair = ConfigUtils.getIntegerPair(s); - itemCFBuilder.amount(pair.left(), pair.right()); - } else { - itemCFBuilder.amount(section.getInt("amount", 1)); - } - 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()); - if (temp.getType() == Material.AIR) { - return temp; - } - int amount = builder.getAmount(); - temp.setAmount(amount); - NBTItem nbtItem = new NBTItem(temp); - for (ItemBuilder.ItemPropertyEditor editor : builder.getEditors()) { - editor.edit(player, nbtItem, placeholders); - } - ItemUtils.updateNBTItemLore(nbtItem); - return nbtItem.getItem(); - } - - @Override - public ItemStack getItemStackAppearance(Player player, String material) { - if (material != null) { - ItemStack itemStack = buildAnyPluginItemByID(player, material); - if (itemStack != null) { - NBTItem nbtItem = new NBTItem(itemStack); - nbtItem.removeKey("display"); - return nbtItem.getItem(); - } else { - return new ItemStack(Material.BARRIER); - } - } else { - return new ItemStack(Material.STRUCTURE_VOID); - } - } - - /** - * 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; - itemLibraryMap.put(itemLibrary.identification(), itemLibrary); - this.resetItemDetectionOrder(); - 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(String identification) { - boolean success = itemLibraryMap.remove(identification) != null; - if (success) - this.resetItemDetectionOrder(); - return success; - } - - @Override - public void dropItem(Player player, Location hookLocation, Location playerLocation, ItemStack item, Condition condition) { - if (item.getType() == Material.AIR) { - return; - } - - if (CFConfig.enableFishingBag && plugin.getBagManager().doesBagStoreLoots() && RequirementManager.isRequirementMet(condition, plugin.getBagManager().getCollectRequirements())) { - var bag = plugin.getBagManager().getOnlineBagInventory(player.getUniqueId()); - - FishingBagPreCollectEvent preCollectEvent = new FishingBagPreCollectEvent(player, item, bag); - Bukkit.getPluginManager().callEvent(preCollectEvent); - if (preCollectEvent.isCancelled()) { - return; - } - - int cannotPut = ItemUtils.putLootsToBag(bag, item, item.getAmount()); - // some are put into bag - if (cannotPut != item.getAmount()) { - ActionManager.triggerActions(condition, plugin.getBagManager().getCollectLootActions()); - } - // all are put - if (cannotPut == 0) { - return; - } - // bag is full - item.setAmount(cannotPut); - ActionManager.triggerActions(condition, plugin.getBagManager().getBagFullActions()); - } - - FishingLootPreSpawnEvent preSpawnEvent = new FishingLootPreSpawnEvent(player, hookLocation, item); - Bukkit.getPluginManager().callEvent(preSpawnEvent); - if (preSpawnEvent.isCancelled()) { - return; - } - - Item itemEntity = hookLocation.getWorld().dropItem(hookLocation, item); - FishingLootSpawnEvent spawnEvent = new FishingLootSpawnEvent(player, hookLocation, itemEntity); - Bukkit.getPluginManager().callEvent(spawnEvent); - if (spawnEvent.isCancelled()) { - itemEntity.remove(); - return; - } - - itemEntity.setInvulnerable(true); - plugin.getScheduler().runTaskAsyncLater(() -> { - if (itemEntity.isValid()) { - itemEntity.setInvulnerable(false); - } - }, 1, TimeUnit.SECONDS); - - Vector vector = playerLocation.subtract(hookLocation).toVector().multiply(0.105); - vector = vector.setY((vector.getY() + 0.22) * 1.18); - itemEntity.setVelocity(vector); - } - - /** - * Decreases the durability of an ItemStack by a specified amount and optionally updates its lore. - * - * @param player Player - * @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(Player player, ItemStack itemStack, int amount, boolean updateLore) { - ItemUtils.decreaseDurability(player, 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); - } - - public static class CFBuilder implements ItemBuilder { - - private final String library; - private final String id; - private int min_amount; - private int max_amount; - private final LinkedHashMap editors; - - public CFBuilder(String library, String id) { - this.id = id; - this.library = library; - this.editors = new LinkedHashMap<>(); - this.min_amount = (max_amount = 1); - } - - public static CFBuilder of(String library, String id) { - return new CFBuilder(library, id); - } - - @Override - public ItemStack build(Player player, Map placeholders) { - return ItemManagerImpl.instance.build(player, this, placeholders); - } - - @Override - public boolean persist() { - return false; - } - - @Override - public ItemBuilder customModelData(int value) { - if (value == 0) return this; - editors.put("custom-model-data", (player, nbtItem, placeholders) -> nbtItem.setInteger("CustomModelData", value)); - return this; - } - - @Override - public ItemBuilder name(String name) { - if (name == null) return this; - editors.put("name", (player, nbtItem, placeholders) -> { - NBTCompound displayCompound = nbtItem.getOrCreateCompound("display"); - displayCompound.setString("Name", AdventureHelper.getInstance().componentToJson( - AdventureHelper.getInstance().getComponentFromMiniMessage( - "" + PlaceholderManagerImpl.getInstance().parse(player, name, placeholders) - ) - )); - }); - return this; - } - - @Override - public ItemBuilder amount(int amount) { - this.min_amount = amount; - this.max_amount = amount; - return this; - } - - @Override - public ItemBuilder amount(int min_amount, int max_amount) { - this.min_amount = min_amount; - this.max_amount = max_amount; - return this; - } - - @Override - public ItemBuilder tag(boolean tag, String type, String id) { - editors.put("tag", (player, nbtItem, placeholders) -> { - if (!tag) return; - NBTCompound cfCompound = nbtItem.getOrCreateCompound("CustomFishing"); - cfCompound.setString("type", type); - cfCompound.setString("id", id); - }); - return this; - } - - @Override - public ItemBuilder unbreakable(boolean unbreakable) { - editors.put("unbreakable", (player, nbtItem, placeholders) -> { - if (!unbreakable) return; - nbtItem.setByte("Unbreakable", (byte) 1); - }); - return this; - } - - @Override - public ItemBuilder placeable(boolean placeable) { - editors.put("placeable", (player, nbtItem, placeholders) -> { - if (!placeable) return; - NBTCompound cfCompound = nbtItem.getOrCreateCompound("CustomFishing"); - cfCompound.setByte("placeable", (byte) 1); - }); - return this; - } - - @Override - public ItemBuilder lore(List lore) { - if (lore.size() == 0) return this; - editors.put("lore", (player, nbtItem, placeholders) -> { - NBTCompound displayCompound = nbtItem.getOrCreateCompound("display"); - NBTList list = displayCompound.getStringList("Lore"); - list.clear(); - list.addAll(lore.stream().map(s -> AdventureHelper.getInstance().componentToJson( - AdventureHelper.getInstance().getComponentFromMiniMessage( - "" + PlaceholderManagerImpl.getInstance().parse(player, s, placeholders) - ) - )).toList()); - }); - return this; - } - - @Override - public ItemBuilder nbt(Map nbt) { - if (nbt.size() == 0) return this; - editors.put("nbt", (player, nbtItem, placeholders) -> NBTUtils.setTagsFromBukkitYAML(player, placeholders, nbtItem, nbt)); - return this; - } - - @Override - public ItemBuilder nbt(ConfigurationSection section) { - if (section == null) return this; - editors.put("nbt", (player, nbtItem, placeholders) -> NBTUtils.setTagsFromBukkitYAML(player, placeholders, nbtItem, section.getValues(false))); - return this; - } - - @Override - public ItemBuilder itemFlag(List itemFlags) { - if (itemFlags.size() == 0) return this; - editors.put("item-flag", (player, nbtItem, placeholders) -> { - int flag = 0; - for (ItemFlag itemFlag : itemFlags) { - flag = flag | 1 << itemFlag.ordinal(); - } - nbtItem.setInteger("HideFlags", flag); - }); - return this; - } - - @Override - public ItemBuilder enchantment(List> enchantments, boolean store) { - if (enchantments.size() == 0) return this; - editors.put("enchantment", (player, nbtItem, placeholders) -> { - NBTCompoundList list = nbtItem.getCompoundList(store ? "StoredEnchantments" : "Enchantments"); - for (Pair pair : enchantments) { - NBTCompound nbtCompound = list.addCompound(); - nbtCompound.setString("id", pair.left()); - nbtCompound.setShort("lvl", pair.right()); - } - }); - return this; - } - - @Override - public ItemBuilder randomEnchantments(List> enchantments, boolean store) { - if (enchantments.size() == 0) return this; - editors.put("random-enchantment", (player, nbtItem, placeholders) -> { - NBTCompoundList list = nbtItem.getCompoundList(store ? "StoredEnchantments" : "Enchantments"); - HashSet ids = new HashSet<>(); - for (Tuple pair : enchantments) { - if (Math.random() < pair.getLeft() && !ids.contains(pair.getMid())) { - NBTCompound nbtCompound = list.addCompound(); - nbtCompound.setString("id", pair.getMid()); - nbtCompound.setShort("lvl", pair.getRight()); - ids.add(pair.getMid()); - } - } - }); - return this; - } - - @Override - public ItemBuilder enchantmentPool(List> amountPairs, List, Value>> enchantments, boolean store) { - if (enchantments.size() == 0 || amountPairs.size() == 0) return this; - editors.put("enchantment-pool", (player, nbtItem, placeholders) -> { - List> parsedAmountPair = new ArrayList<>(amountPairs.size()); - Map map = new HashMap<>(); - for (Pair rawValue : amountPairs) { - parsedAmountPair.add(Pair.of(rawValue.left(), rawValue.right().get(player, map))); - } - - int amount = WeightUtils.getRandom(parsedAmountPair); - if (amount <= 0) return; - NBTCompoundList list = nbtItem.getCompoundList(store ? "StoredEnchantments" : "Enchantments"); - - HashSet addedEnchantments = new HashSet<>(); - - List, Double>> cloned = new ArrayList<>(enchantments.size()); - for (Pair, Value> rawValue : enchantments) { - cloned.add(Pair.of(rawValue.left(), rawValue.right().get(player, map))); - } - - int i = 0; - outer: - while (i < amount && cloned.size() != 0) { - Pair enchantPair = WeightUtils.getRandom(cloned); - Enchantment enchantment = Enchantment.getByKey(NamespacedKey.fromString(enchantPair.left())); - if (enchantment == null) { - throw new NullPointerException("Enchantment: " + enchantPair.left() + " doesn't exist on your server."); - } - for (Enchantment added : addedEnchantments) { - if (enchantment.conflictsWith(added)) { - cloned.removeIf(pair -> pair.left().left().equals(enchantPair.left())); - continue outer; - } - } - NBTCompound nbtCompound = list.addCompound(); - nbtCompound.setString("id", enchantPair.left()); - nbtCompound.setShort("lvl", enchantPair.right()); - addedEnchantments.add(enchantment); - cloned.removeIf(pair -> pair.left().left().equals(enchantPair.left())); - i++; - } - }); - return this; - } - - @Override - public ItemBuilder maxDurability(int max) { - if (max == 0) return this; - editors.put("durability", (player, nbtItem, placeholders) -> { - NBTCompound cfCompound = nbtItem.getOrCreateCompound("CustomFishing"); - cfCompound.setInteger("max_dur", max); - cfCompound.setInteger("cur_dur", max); - }); - return this; - } - - @Override - public ItemBuilder price(float base, float bonus) { - if (base == 0 && bonus == 0) return this; - editors.put("price", (player, nbtItem, placeholders) -> { - placeholders.put("{base}", String.format("%.2f", base)); - placeholders.put("{BASE}", String.valueOf(base)); - placeholders.put("{bonus}", String.format("%.2f", bonus)); - placeholders.put("{BONUS}", String.valueOf(bonus)); - double price; - price = CustomFishingPlugin.get().getMarketManager().getFishPrice( - player, - placeholders - ); - nbtItem.setDouble("Price", price); - placeholders.put("{price}", String.format("%.2f", price)); - placeholders.put("{PRICE}", String.valueOf(price)); - }); - return this; - } - - @Override - public ItemBuilder size(Pair size) { - if (size == null) return this; - editors.put("size", (player, nbtItem, placeholders) -> { - NBTCompound cfCompound = nbtItem.getOrCreateCompound("CustomFishing"); - float random = size.left() + (size.left() >= size.right() ? 0 : ThreadLocalRandom.current().nextFloat(size.right() - size.left())); - float bonus = Float.parseFloat(placeholders.getOrDefault("{size-multiplier}", "1.0")); - double fixed = Double.parseDouble(placeholders.getOrDefault("{size-fixed}", "0.0")); - random *= bonus; - random += fixed; - if (CFConfig.restrictedSizeRange) { - if (random > size.right()) { - random = size.right(); - } else if (random < size.left()) { - random = size.left(); - } - } - cfCompound.setFloat("size", random); - placeholders.put("{size}", String.format("%.2f", random)); - placeholders.put("{SIZE}", String.valueOf(random)); - placeholders.put("{min_size}", String.valueOf(size.left())); - placeholders.put("{max_size}", String.valueOf(size.right())); - }); - return this; - } - - @Override - public ItemBuilder stackable(boolean stackable) { - if (stackable) return this; - editors.put("stackable", (player, nbtItem, placeholders) -> { - NBTCompound cfCompound = nbtItem.getOrCreateCompound("CustomFishing"); - cfCompound.setUUID("uuid", UUID.randomUUID()); - }); - return this; - } - - @Override - public ItemBuilder preventGrabbing(boolean prevent) { - if (!prevent) return this; - editors.put("grabbing", (player, nbtItem, placeholders) -> { - nbtItem.setString("owner", placeholders.get("player")); - }); - return this; - } - - @Override - public ItemBuilder head(String base64) { - if (base64 == null) return this; - editors.put("head", (player, nbtItem, placeholders) -> { - NBTCompound nbtCompound = nbtItem.addCompound("SkullOwner"); - nbtCompound.setUUID("Id", UUID.nameUUIDFromBytes(id.getBytes())); - NBTListCompound texture = nbtCompound.addCompound("Properties").getCompoundList("textures").addCompound(); - texture.setString("Value", base64); - }); - return this; - } - - @Override - public ItemBuilder randomDamage(boolean damage) { - if (!damage) return this; - editors.put("damage", (player, nbtItem, placeholders) -> { - NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); - if (cfCompound != null) { - int i = cfCompound.getInteger("max_dur"); - if (i != 0) { - int dur = ThreadLocalRandom.current().nextInt(i); - cfCompound.setInteger("cur_dur", dur); - nbtItem.setInteger("Damage", (int) (nbtItem.getItem().getType().getMaxDurability() * ((double) dur / i))); - } else { - nbtItem.setInteger("Damage", ThreadLocalRandom.current().nextInt(nbtItem.getItem().getType().getMaxDurability())); - } - } else { - nbtItem.setInteger("Damage", ThreadLocalRandom.current().nextInt(nbtItem.getItem().getType().getMaxDurability())); - } - }); - return this; - } - - @Override - public @NotNull String getId() { - return id; - } - - @Override - public @NotNull String getLibrary() { - return library; - } - - @Override - public int getAmount() { - return ThreadLocalRandom.current().nextInt(min_amount, max_amount + 1); - } - - @Override - public Collection getEditors() { - return editors.values(); - } - - @Override - public ItemBuilder removeEditor(String type) { - editors.remove(type); - return this; - } - - @Override - public ItemBuilder registerCustomEditor(String type, ItemPropertyEditor editor) { - editors.put(type, editor); - return this; - } - } - - /** - * Handles item pickup by players. - * - * @param event The PlayerAttemptPickupItemEvent. - */ - @EventHandler (ignoreCancelled = true) - public void onPickUp(PlayerAttemptPickupItemEvent event) { - ItemStack itemStack = event.getItem().getItemStack(); - NBTItem nbtItem = new NBTItem(itemStack); - if (!nbtItem.hasTag("owner")) return; - if (!Objects.equals(nbtItem.getString("owner"), event.getPlayer().getName())) { - event.setCancelled(true); - } else { - nbtItem.removeKey("owner"); - itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); - } - } - - /** - * Handles item movement in inventories. - * - * @param event The InventoryPickupItemEvent. - */ - @EventHandler (ignoreCancelled = true) - public void onInvPickItem(InventoryPickupItemEvent event) { - ItemStack itemStack = event.getItem().getItemStack(); - NBTItem nbtItem = new NBTItem(itemStack); - if (!nbtItem.hasTag("owner")) return; - nbtItem.removeKey("owner"); - itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); - } - - /** - * Handles item consumption by players. - * - * @param event The PlayerItemConsumeEvent. - */ - @EventHandler (ignoreCancelled = true) - public void onConsumeItem(PlayerItemConsumeEvent event) { - ItemStack itemStack = event.getItem(); - String id = getAnyPluginItemID(itemStack); - Loot loot = plugin.getLootManager().getLoot(id); - if (loot != null) { - Condition condition = new Condition(event.getPlayer()); - if (!loot.disableGlobalAction()) - GlobalSettings.triggerLootActions(ActionTrigger.CONSUME, condition); - loot.triggerActions(ActionTrigger.CONSUME, condition); - } - } - - /** - * Handles the repair of custom items in an anvil. - * - * @param event The PrepareAnvilEvent. - */ - @EventHandler (ignoreCancelled = true) - public void onRepairItem(PrepareAnvilEvent event) { - ItemStack result = event.getInventory().getResult(); - if (result == null || result.getType() == Material.AIR) return; - NBTItem nbtItem = new NBTItem(result); - NBTCompound compound = nbtItem.getCompound("CustomFishing"); - if (compound == null || !compound.hasTag("max_dur")) return; - if (!(result.getItemMeta() instanceof Damageable damageable)) { - return; - } - int max_dur = compound.getInteger("max_dur"); - compound.setInteger("cur_dur", (int) (max_dur * (1 - (double) damageable.getDamage() / result.getType().getMaxDurability()))); - event.setResult(nbtItem.getItem()); - } - - /** - * Handles the mending of custom items. - * - * @param event The PlayerItemMendEvent. - */ - @EventHandler (ignoreCancelled = true) - public void onMending(PlayerItemMendEvent event) { - ItemStack itemStack = event.getItem(); - NBTItem nbtItem = new NBTItem(itemStack); - NBTCompound compound = nbtItem.getCompound("CustomFishing"); - if (compound == null) return; - event.setCancelled(true); - ItemUtils.increaseDurability(itemStack, event.getRepairAmount(), true); - } - - /** - * Handles interactions with custom utility items. - * - * @param event The PlayerInteractEvent. - */ - @EventHandler - public void onInteractWithItems(PlayerInteractEvent event) { - if (event.useItemInHand() == Event.Result.DENY) - return; - if (event.getHand() != EquipmentSlot.HAND) - return; - ItemStack itemStack = event.getPlayer().getInventory().getItemInMainHand(); - if (itemStack.getType() == Material.AIR) - return; - if (event.getAction() != org.bukkit.event.block.Action.RIGHT_CLICK_AIR && event.getAction() != org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK) - return; - String id = getAnyPluginItemID(itemStack); - Condition condition = new Condition(event.getPlayer()); - - Loot loot = plugin.getLootManager().getLoot(id); - if (loot != null) { - loot.triggerActions(ActionTrigger.INTERACT, condition); - return; - } - - // because the id can be from other plugins, so we can't infer the type of the item - for (String type : List.of("util", "bait", "hook")) { - EffectCarrier carrier = plugin.getEffectManager().getEffectCarrier(type, id); - if (carrier != null) { - Action[] actions = carrier.getActions(ActionTrigger.INTERACT); - if (actions != null) - for (Action action : actions) { - action.trigger(condition); - } - break; - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onPlaceBlock(BlockPlaceEvent event) { - ItemStack itemStack = event.getItemInHand(); - if (itemStack.getType() == Material.AIR || itemStack.getAmount() == 0 || !itemStack.hasItemMeta()) { - return; - } - - NBTItem nbtItem = new NBTItem(itemStack); - NBTCompound compound = nbtItem.getCompound("CustomFishing"); - if (compound != null) { - - if (!compound.hasTag("placeable") || compound.getByte("placeable") != 1) { - event.setCancelled(true); - return; - } - - Block block = event.getBlock(); - if (block.getState() instanceof Skull) { - PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer(); - ItemStack cloned = itemStack.clone(); - cloned.setAmount(1); - pdc.set(new NamespacedKey(plugin, LocationUtils.toChunkPosString(block.getLocation())), PersistentDataType.STRING, ItemUtils.toBase64(cloned)); - } else { - event.setCancelled(true); - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onBreakBlock(BlockBreakEvent event) { - final Block block = event.getBlock(); - if (block.getState() instanceof Skull) { - PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer(); - String base64 = pdc.get(new NamespacedKey(plugin, LocationUtils.toChunkPosString(block.getLocation())), PersistentDataType.STRING); - if (base64 != null) { - ItemStack itemStack = ItemUtils.fromBase64(base64); - event.setDropItems(false); - block.getLocation().getWorld().dropItemNaturally(block.getLocation(), itemStack); - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onPiston(BlockPistonExtendEvent event) { - for (Block block : event.getBlocks()) { - if (block.getState() instanceof Skull) { - PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer(); - if (pdc.has(new NamespacedKey(plugin, LocationUtils.toChunkPosString(block.getLocation())), PersistentDataType.STRING)) { - event.setCancelled(true); - return; - } - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onPiston(BlockPistonRetractEvent event) { - for (Block block : event.getBlocks()) { - if (block.getState() instanceof Skull) { - PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer(); - if (pdc.has(new NamespacedKey(plugin, LocationUtils.toChunkPosString(block.getLocation())), PersistentDataType.STRING)) { - event.setCancelled(true); - return; - } - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onExplosion(BlockExplodeEvent event) { - handleExplode(event.blockList()); - } - - @EventHandler (ignoreCancelled = true) - public void onExplosion(EntityExplodeEvent event) { - handleExplode(event.blockList()); - } - - private void handleExplode(List blocks) { - ArrayList blockToRemove = new ArrayList<>(); - for (Block block : blocks) { - if (block.getState() instanceof Skull) { - PersistentDataContainer pdc = block.getChunk().getPersistentDataContainer(); - var nk = new NamespacedKey(plugin, LocationUtils.toChunkPosString(block.getLocation())); - String base64 = pdc.get(nk, PersistentDataType.STRING); - if (base64 != null) { - ItemStack itemStack = ItemUtils.fromBase64(base64); - block.getLocation().getWorld().dropItemNaturally(block.getLocation(), itemStack); - blockToRemove.add(block); - block.setType(Material.AIR); - pdc.remove(nk); - } - } - } - blocks.removeAll(blockToRemove); - } -} \ No newline at end of file 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 deleted file mode 100644 index d2f0dd86..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/loot/LootManagerImpl.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -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.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.misc.WeightModifier; -import net.momirealms.customfishing.api.mechanic.statistic.StatisticsKey; -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.setting.CFConfig; -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; -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) { - this.plugin = plugin; - this.lootMap = new HashMap<>(); - this.lootGroupMap = new HashMap<>(); - } - - public void load() { - this.loadLootsFromPluginFolder(); - } - - public void unload() { - this.lootMap.clear(); - this.lootGroupMap.clear(); - } - - public void disable() { - 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<>(); - for (String type : List.of("item", "entity", "block")) { - File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type); - if (!typeFolder.exists()) { - if (!typeFolder.mkdirs()) return; - plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false); - } - fileDeque.push(typeFolder); - while (!fileDeque.isEmpty()) { - File file = fileDeque.pop(); - File[] files = file.listFiles(); - if (files == null) continue; - for (File subFile : files) { - if (subFile.isDirectory()) { - fileDeque.push(subFile); - } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { - loadSingleFile(subFile, type); - } - } - } - } - } - - /** - * 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 ((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 effect 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 effect, Condition condition) { - Map lootWithWeight = ((RequirementManagerImpl) plugin.getRequirementManager()).getLootWithWeight(condition); - Player player = condition.getPlayer(); - for (Pair pair : effect.getWeightModifier()) { - Double previous = lootWithWeight.get(pair.left()); - if (previous != null) - lootWithWeight.put(pair.left(), pair.right().modify(player, previous)); - } - for (Pair pair : effect.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 effect 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 effect, Condition condition) { - String key = WeightUtils.getRandom(getPossibleLootKeysWithWeight(effect, condition)); - if (key == null) { - return null; - } - Loot loot = getLoot(key); - if (loot == null) { - LogUtils.warn(String.format("Loot %s doesn't exist in any of the subfolders[item/entity/block].", 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()) { - if (entry.getValue() instanceof ConfigurationSection section) { - var loot = getSingleSectionItem( - file.getPath(), - section, - 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<>()); - groupMembers.add(loot.getID()); - } - } - // legacy format support - if (section.contains("requirements") && section.contains("weight")) { - plugin.getRequirementManager().putLegacyLootToMap( - loot.getID(), - plugin.getRequirementManager().getRequirements(section.getConfigurationSection("requirements"), false), - section.getDouble("weight", 0) - ); - } - } - } - } - - /** - * 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(String filePath, ConfigurationSection section, String namespace, String key) { - return new CFLoot.Builder(key, LootType.valueOf(namespace.toUpperCase(Locale.ENGLISH))) - .filePath(filePath) - .disableStats(section.getBoolean("disable-stat", CFConfig.globalDisableStats)) - .disableGames(section.getBoolean("disable-game", CFConfig.globalDisableGame)) - .instantGame(section.getBoolean("instant-game", CFConfig.globalInstantGame)) - .showInFinder(section.getBoolean("show-in-fishfinder", CFConfig.globalShowInFinder)) - .disableGlobalActions(section.getBoolean("disable-global-event", false)) - .score(section.getDouble("score")) - .baseEffect(plugin.getEffectManager().getBaseEffect(section.getConfigurationSection("effects"))) - .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(plugin.getActionManager().getTimesActionMap(section.getConfigurationSection("events.success-times"))) - .statsKey(new StatisticsKey(section.getString("statistics.amount", key), section.getString("statistics.size", key))) - .build(); - } -} 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 deleted file mode 100644 index 17b75777..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUI.java +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.market; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.EarningData; -import net.momirealms.customfishing.api.mechanic.market.MarketGUIHolder; -import net.momirealms.customfishing.api.util.InventoryUtils; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.util.ItemUtils; -import net.momirealms.customfishing.util.NumberUtils; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -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; - this.earningData = earningData; - this.itemsCharMap = new HashMap<>(); - this.itemsSlotMap = new HashMap<>(); - var holder = new MarketGUIHolder(); - this.inventory = InventoryUtils.createInventory( - holder, - manager.getLayout().length * 9, - AdventureHelper.getInstance().getComponentFromMiniMessage(manager.getTitle()) - ); - 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()) { - for (int index = 0; index < 9; index++) { - char symbol; - if (index < content.length()) { - symbol = content.charAt(index); - } else { - symbol = ' '; - } - MarketGUIElement element = itemsCharMap.get(symbol); - if (element != null) { - element.addSlot(index + line * 9); - itemsSlotMap.put(index + line * 9, element); - } - } - line++; - } - for (Map.Entry entry : itemsSlotMap.entrySet()) { - this.inventory.setItem(entry.getKey(), entry.getValue().getItemStack().clone()); - } - } - - /** - * 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); - } - 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 earningLimit = manager.getEarningLimit(owner); - MarketDynamicGUIElement sellElement = (MarketDynamicGUIElement) getElement(manager.getSellSlot()); - if (sellElement != null && sellElement.getSlots().size() > 0) { - double totalWorth = getTotalWorthInMarketGUI(); - int soldAmount = getSoldAmount(); - if (totalWorth <= 0) { - sellElement.setItemStack( - manager.getSellIconDenyBuilder().build(owner, - Map.of("{money}", NumberUtils.money(totalWorth) - ,"{money_formatted}", String.format("%.2f", totalWorth) - ,"{player}", owner.getName() - ,"{rest}", NumberUtils.money(earningLimit - earningData.earnings) - ,"{rest_formatted}", String.format("%.2f", (earningLimit - earningData.earnings)) - ,"{sold-item-amount}", String.valueOf(soldAmount) - ) - ) - ); - } else if (earningLimit != -1 && (earningLimit - earningData.earnings < totalWorth)) { - sellElement.setItemStack( - manager.getSellIconLimitBuilder().build(owner, - Map.of("{money}", NumberUtils.money(totalWorth) - ,"{money_formatted}", String.format("%.2f", totalWorth) - ,"{player}", owner.getName() - ,"{rest}", NumberUtils.money(earningLimit - earningData.earnings) - ,"{rest_formatted}", String.format("%.2f", (earningLimit - earningData.earnings)) - ,"{sold-item-amount}", String.valueOf(soldAmount) - ) - ) - ); - } else { - sellElement.setItemStack( - manager.getSellIconAllowBuilder().build(owner, - Map.of("{money}", NumberUtils.money(totalWorth) - ,"{money_formatted}", String.format("%.2f", totalWorth) - ,"{player}", owner.getName() - ,"{rest}", NumberUtils.money(earningLimit - earningData.earnings) - ,"{rest_formatted}", String.format("%.2f", (earningLimit - earningData.earnings)) - ,"{sold-item-amount}", String.valueOf(soldAmount) - ) - ) - ); - } - } - - MarketDynamicGUIElement sellAllElement = (MarketDynamicGUIElement) getElement(manager.getSellAllSlot()); - if (sellAllElement != null && sellAllElement.getSlots().size() > 0) { - double totalWorth = manager.getInventoryTotalWorth(owner.getInventory()); - int sellAmount = manager.getInventorySellAmount(owner.getInventory()); - if (manager.sellFishingBag() && CustomFishingPlugin.get().getBagManager().isEnabled()) { - Inventory bag = CustomFishingPlugin.get().getBagManager().getOnlineBagInventory(owner.getUniqueId()); - if (bag != null) { - totalWorth += manager.getInventoryTotalWorth(bag); - sellAmount += manager.getInventorySellAmount(bag); - } - } - if (totalWorth <= 0) { - sellAllElement.setItemStack( - manager.getSellAllIconDenyBuilder().build(owner, - Map.of("{money}", NumberUtils.money(totalWorth) - ,"{money_formatted}", String.format("%.2f", totalWorth) - ,"{player}", owner.getName() - ,"{rest}", NumberUtils.money(earningLimit - earningData.earnings) - ,"{rest_formatted}", String.format("%.2f", (earningLimit - earningData.earnings)) - ,"{sold-item-amount}", String.valueOf(sellAmount) - ) - ) - ); - } else if (earningLimit != -1 && (earningLimit - earningData.earnings < totalWorth)) { - sellAllElement.setItemStack( - manager.getSellAllIconLimitBuilder().build(owner, - Map.of("{money}", NumberUtils.money(totalWorth) - ,"{money_formatted}", String.format("%.2f", totalWorth) - ,"{player}", owner.getName() - ,"{rest}", NumberUtils.money(earningLimit - earningData.earnings) - ,"{rest_formatted}", String.format("%.2f", (earningLimit - earningData.earnings)) - ,"{sold-item-amount}", String.valueOf(sellAmount) - ) - ) - ); - } else { - sellAllElement.setItemStack( - manager.getSellAllIconAllowBuilder().build(owner, - Map.of("{money}", NumberUtils.money(totalWorth) - ,"{money_formatted}", String.format("%.2f", totalWorth) - ,"{player}", owner.getName() - ,"{rest}", NumberUtils.money(earningLimit - earningData.earnings) - ,"{rest_formatted}", String.format("%.2f", (earningLimit - earningData.earnings)) - ,"{sold-item-amount}", String.valueOf(sellAmount) - ) - ) - ); - } - } - for (Map.Entry entry : itemsSlotMap.entrySet()) { - if (entry.getValue() instanceof MarketDynamicGUIElement dynamicGUIElement) { - this.inventory.setItem(entry.getKey(), dynamicGUIElement.getItemStack().clone()); - } - } - return this; - } - - /** - * Calculate and return the total worth of items in the inventory. - * @return The total worth of items. - */ - public double getTotalWorthInMarketGUI() { - double money = 0d; - MarketGUIElement itemElement = getElement(manager.getItemSlot()); - if (itemElement == null) { - LogUtils.warn("No item slot available. Please check if GUI layout contains the item slot symbol."); - return money; - } - for (int slot : itemElement.getSlots()) { - money += manager.getItemPrice(this.inventory.getItem(slot)); - } - 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) { - return; - } - for (int slot : itemElement.getSlots()) { - double money = manager.getItemPrice(inventory.getItem(slot)); - if (money != 0) { - inventory.setItem(slot, new ItemStack(Material.AIR)); - } - } - } - - /** - * 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) { - return -1; - } - for (int slot : itemElement.getSlots()) { - ItemStack itemStack = inventory.getItem(slot); - if (itemStack == null || itemStack.getType() == Material.AIR) { - return slot; - } - } - return -1; - } - - /** - * Return items to the owner's inventory. - */ - public void returnItems() { - MarketGUIElement itemElement = getElement(manager.getItemSlot()); - if (itemElement == null) { - return; - } - for (int slot : itemElement.getSlots()) { - ItemStack itemStack = inventory.getItem(slot); - if (itemStack != null && itemStack.getType() != Material.AIR) { - ItemUtils.giveItem(owner, itemStack, itemStack.getAmount()); - inventory.setItem(slot, new ItemStack(Material.AIR)); - } - } - } - - /** - * Get the earning data associated with this MarketGUI. - * @return The EarningData object. - */ - public EarningData getEarningData() { - return earningData; - } - - public int getSoldAmount() { - int amount = 0; - MarketGUIElement itemElement = getElement(manager.getItemSlot()); - if (itemElement == null) { - return amount; - } - for (int slot : itemElement.getSlots()) { - ItemStack itemStack = inventory.getItem(slot); - double money = manager.getItemPrice(itemStack); - if (money > 0 && itemStack != null) { - amount += itemStack.getAmount(); - } - } - return amount; - } -} 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 deleted file mode 100644 index cad0fdac..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java +++ /dev/null @@ -1,637 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.market; - -import de.tr7zw.changeme.nbtapi.NBTItem; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.EarningData; -import net.momirealms.customfishing.api.data.user.OnlineUser; -import net.momirealms.customfishing.api.manager.MarketManager; -import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.item.BuildableItem; -import net.momirealms.customfishing.api.mechanic.market.MarketGUIHolder; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; -import net.momirealms.customfishing.util.ConfigUtils; -import net.momirealms.customfishing.util.NumberUtils; -import net.objecthunter.exp4j.ExpressionBuilder; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; -import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -public class MarketManagerImpl implements MarketManager, Listener { - - private final CustomFishingPlugin plugin; - private final HashMap priceMap; - private String[] layout; - private String title; - private String formula; - private final HashMap decorativeIcons; - private char itemSlot; - private char sellSlot; - private char sellAllSlot; - private BuildableItem sellIconAllowBuilder; - private BuildableItem sellIconDenyBuilder; - private BuildableItem sellIconLimitBuilder; - private BuildableItem sellAllIconAllowBuilder; - private BuildableItem sellAllIconDenyBuilder; - private BuildableItem sellAllIconLimitBuilder; - private Action[] sellDenyActions; - private Action[] sellAllowActions; - private Action[] sellLimitActions; - private Action[] sellAllDenyActions; - private Action[] sellAllAllowActions; - private Action[] sellAllLimitActions; - private String earningLimitExpression; - private boolean allowItemWithNoPrice; - private boolean sellFishingBag; - private final ConcurrentHashMap marketGUIMap; - private boolean enable; - private CancellableTask resetEarningsTask; - private int date; - - public MarketManagerImpl(CustomFishingPlugin plugin) { - this.plugin = plugin; - this.priceMap = new HashMap<>(); - this.decorativeIcons = new HashMap<>(); - this.marketGUIMap = new ConcurrentHashMap<>(); - this.date = getDate(); - } - - public void load() { - this.loadConfig(); - Bukkit.getPluginManager().registerEvents(this, plugin); - if (!enable) return; - this.resetEarningsTask = plugin.getScheduler().runTaskAsyncTimer(() -> { - int now = getDate(); - if (this.date != now) { - this.date = now; - for (OnlineUser onlineUser : plugin.getStorageManager().getOnlineUsers()) { - onlineUser.getEarningData().date = now; - onlineUser.getEarningData().earnings = 0d; - } - } - }, 1, 1, TimeUnit.SECONDS); - } - - public void unload() { - HandlerList.unregisterAll(this); - this.priceMap.clear(); - this.decorativeIcons.clear(); - if (this.resetEarningsTask != null && !this.resetEarningsTask.isCancelled()) { - this.resetEarningsTask.cancel(); - this.resetEarningsTask = null; - } - } - - public void disable() { - unload(); - } - - // Load configuration from the plugin's config file - private void loadConfig() { - YamlConfiguration config = plugin.getConfig("market.yml"); - this.enable = config.getBoolean("enable", true); - this.formula = config.getString("price-formula", "{base} + {bonus} * {size}"); - 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.itemSlot = config.getString("item-slot.symbol", "I").charAt(0); - this.allowItemWithNoPrice = config.getBoolean("item-slot.allow-items-with-no-price", true); - - ConfigurationSection sellAllSection = config.getConfigurationSection("sell-all-icons"); - if (sellAllSection != null) { - this.sellAllSlot = sellAllSection.getString("symbol", "S").charAt(0); - this.sellFishingBag = sellAllSection.getBoolean("fishingbag", true); - - this.sellAllIconAllowBuilder = plugin.getItemManager().getItemBuilder(sellAllSection.getConfigurationSection("allow-icon"), "gui", "sell-all"); - this.sellAllIconDenyBuilder = plugin.getItemManager().getItemBuilder(sellAllSection.getConfigurationSection("deny-icon"), "gui", "sell-all"); - this.sellAllIconLimitBuilder = plugin.getItemManager().getItemBuilder(sellAllSection.getConfigurationSection("limit-icon"), "gui", "sell-all"); - - this.sellAllAllowActions = plugin.getActionManager().getActions(sellAllSection.getConfigurationSection("allow-icon.action")); - this.sellAllDenyActions = plugin.getActionManager().getActions(sellAllSection.getConfigurationSection("deny-icon.action")); - this.sellAllLimitActions = plugin.getActionManager().getActions(sellAllSection.getConfigurationSection("limit-icon.action")); - } - - ConfigurationSection sellSection = config.getConfigurationSection("sell-icons"); - if (sellSection == null) { - // for old config compatibility - sellSection = config.getConfigurationSection("functional-icons"); - } - if (sellSection != null) { - this.sellSlot = sellSection.getString("symbol", "B").charAt(0); - - this.sellIconAllowBuilder = plugin.getItemManager().getItemBuilder(sellSection.getConfigurationSection("allow-icon"), "gui", "allow"); - this.sellIconDenyBuilder = plugin.getItemManager().getItemBuilder(sellSection.getConfigurationSection("deny-icon"), "gui", "deny"); - this.sellIconLimitBuilder = plugin.getItemManager().getItemBuilder(sellSection.getConfigurationSection("limit-icon"), "gui", "limit"); - - this.sellAllowActions = plugin.getActionManager().getActions(sellSection.getConfigurationSection("allow-icon.action")); - this.sellDenyActions = plugin.getActionManager().getActions(sellSection.getConfigurationSection("deny-icon.action")); - this.sellLimitActions = plugin.getActionManager().getActions(sellSection.getConfigurationSection("limit-icon.action")); - } - - this.earningLimitExpression = config.getBoolean("limitation.enable", true) ? config.getString("limitation.earnings", "10000") : "-1"; - - // 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()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - char symbol = Objects.requireNonNull(innerSection.getString("symbol")).charAt(0); - var builder = plugin.getItemManager().getItemBuilder(innerSection, "gui", entry.getKey()); - decorativeIcons.put(symbol, builder); - } - } - } - } - - /** - * 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."); - return; - } - - MarketGUI gui = new MarketGUI(this, player, user.getEarningData()); - gui.addElement(new MarketGUIElement(getItemSlot(), new ItemStack(Material.AIR))); - gui.addElement(new MarketDynamicGUIElement(getSellSlot(), new ItemStack(Material.AIR))); - gui.addElement(new MarketDynamicGUIElement(getSellAllSlot(), new ItemStack(Material.AIR))); - for (Map.Entry entry : decorativeIcons.entrySet()) { - gui.addElement(new MarketGUIElement(entry.getKey(), entry.getValue().build(player))); - } - gui.build().refresh().show(player); - 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)) - return; - if (!(event.getInventory().getHolder() instanceof MarketGUIHolder)) - return; - MarketGUI gui = marketGUIMap.remove(player.getUniqueId()); - if (gui != null) - 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()); - if (gui != null) - 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()) - return; - Inventory inventory = event.getInventory(); - if (!(inventory.getHolder() instanceof MarketGUIHolder)) - return; - Player player = (Player) event.getWhoClicked(); - MarketGUI gui = marketGUIMap.get(player.getUniqueId()); - if (gui == null) { - event.setCancelled(true); - player.closeInventory(); - return; - } - - MarketGUIElement element = gui.getElement(itemSlot); - if (element == null) { - event.setCancelled(true); - return; - } - - List slots = element.getSlots(); - for (int dragSlot : event.getRawSlots()) { - if (!slots.contains(dragSlot)) { - event.setCancelled(true); - return; - } - } - - 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; - - MarketGUI gui = marketGUIMap.get(player.getUniqueId()); - if (gui == null) { - event.setCancelled(true); - player.closeInventory(); - return; - } - - if (clickedInv != player.getInventory()) { - EarningData data = gui.getEarningData(); - if (data.date != getCachedDate()) { - data.date = getCachedDate(); - data.earnings = 0; - } - - int slot = event.getSlot(); - MarketGUIElement element = gui.getElement(slot); - if (element == null) { - event.setCancelled(true); - return; - } - - if (element.getSymbol() != itemSlot) { - event.setCancelled(true); - } - - if (element.getSymbol() == sellSlot) { - double worth = gui.getTotalWorthInMarketGUI(); - int amount = gui.getSoldAmount(); - double earningLimit = getEarningLimit(player); - Condition condition = new Condition(player, new HashMap<>(Map.of( - "{money}", NumberUtils.money(worth), - "{rest}", NumberUtils.money(earningLimit - data.earnings), - "{money_formatted}", String.format("%.2f", worth) - ,"{rest_formatted}", String.format("%.2f", (earningLimit - data.earnings)) - ,"{sold-item-amount}", String.valueOf(amount) - ))); - if (worth > 0) { - if (earningLimit != -1 && (earningLimit - data.earnings) < worth) { - // Can't earn more money - if (getSellLimitActions() != null) { - for (Action action : getSellLimitActions()) { - action.trigger(condition); - } - } - } else { - // Clear items and update earnings - gui.clearWorthyItems(); - data.earnings += worth; - condition.insertArg("{rest}", NumberUtils.money(earningLimit - data.earnings)); - condition.insertArg("{rest_formatted}", String.format("%.2f", (earningLimit - data.earnings))); - if (getSellAllowActions() != null) { - for (Action action : getSellAllowActions()) { - action.trigger(condition); - } - } - } - } else { - // Nothing to sell - if (getSellDenyActions() != null) { - for (Action action : getSellDenyActions()) { - action.trigger(condition); - } - } - } - } else if (element.getSymbol() == sellAllSlot) { - double worth = getInventoryTotalWorth(player.getInventory()); - int amount = getInventorySellAmount(player.getInventory()); - double earningLimit = getEarningLimit(player); - if (sellFishingBag() && CustomFishingPlugin.get().getBagManager().isEnabled()) { - Inventory bag = CustomFishingPlugin.get().getBagManager().getOnlineBagInventory(player.getUniqueId()); - if (bag != null) { - worth += getInventoryTotalWorth(bag); - amount += getInventorySellAmount(bag); - } - } - Condition condition = new Condition(player, new HashMap<>(Map.of( - "{money}", NumberUtils.money(worth), - "{rest}", NumberUtils.money(earningLimit - data.earnings), - "{money_formatted}", String.format("%.2f", worth) - ,"{rest_formatted}", String.format("%.2f", (earningLimit - data.earnings)) - ,"{sold-item-amount}", String.valueOf(amount) - ))); - if (worth > 0) { - if (earningLimit != -1 && (earningLimit - data.earnings) < worth) { - // Can't earn more money - if (getSellAllLimitActions() != null) { - for (Action action : getSellAllLimitActions()) { - action.trigger(condition); - } - } - } else { - // Clear items and update earnings - clearWorthyItems(player.getInventory()); - if (sellFishingBag() && CustomFishingPlugin.get().getBagManager().isEnabled()) { - Inventory bag = CustomFishingPlugin.get().getBagManager().getOnlineBagInventory(player.getUniqueId()); - if (bag != null) { - clearWorthyItems(bag); - } - } - data.earnings += worth; - condition.insertArg("{rest}", NumberUtils.money(earningLimit - data.earnings)); - condition.insertArg("{rest_formatted}", String.format("%.2f", (earningLimit - data.earnings))); - if (getSellAllAllowActions() != null) { - for (Action action : getSellAllAllowActions()) { - action.trigger(condition); - } - } - } - } else { - // Nothing to sell - if (getSellAllDenyActions() != null) { - for (Action action : getSellAllDenyActions()) { - action.trigger(condition); - } - } - } - } - } else { - // Handle interactions with the player's inventory - ItemStack current = event.getCurrentItem(); - if (!allowItemWithNoPrice) { - double price = getItemPrice(current); - if (price <= 0) { - event.setCancelled(true); - return; - } - } - - if ((event.getClick() == ClickType.SHIFT_LEFT || event.getClick() == ClickType.SHIFT_RIGHT) - && (current != null && current.getType() != Material.AIR)) { - event.setCancelled(true); - MarketGUIElement element = gui.getElement(itemSlot); - if (element == null) return; - for (int slot : element.getSlots()) { - ItemStack itemStack = gui.getInventory().getItem(slot); - if (itemStack != null && itemStack.getType() != Material.AIR) { - if (current.getType() == itemStack.getType() - && itemStack.getAmount() != itemStack.getType().getMaxStackSize() - && current.getItemMeta().equals(itemStack.getItemMeta()) - ) { - int left = itemStack.getType().getMaxStackSize() - itemStack.getAmount(); - if (current.getAmount() <= left) { - itemStack.setAmount(itemStack.getAmount() + current.getAmount()); - current.setAmount(0); - break; - } else { - current.setAmount(current.getAmount() - left); - itemStack.setAmount(itemStack.getType().getMaxStackSize()); - } - } - } else { - gui.getInventory().setItem(slot, current.clone()); - current.setAmount(0); - break; - } - } - } - } - - // Refresh the GUI - plugin.getScheduler().runTaskSyncLater(gui::refresh, player.getLocation(), 50, TimeUnit.MILLISECONDS); - } - - @Override - public int getCachedDate() { - return date; - } - - @Override - public int getDate() { - Calendar calendar = Calendar.getInstance(); - return (calendar.get(Calendar.MONTH) +1) * 100 + calendar.get(Calendar.DATE); - } - - @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(); - } - - @Override - public String getFormula() { - return formula; - } - - @Override - public double getFishPrice(Player player, Map vars) { - String temp = PlaceholderManagerImpl.getInstance().parse(player, formula, vars); - var placeholders = PlaceholderManagerImpl.getInstance().detectPlaceholders(temp); - for (String placeholder : placeholders) { - temp = temp.replace(placeholder, "0"); - } - return new ExpressionBuilder(temp).build().evaluate(); - } - - @Override - public char getItemSlot() { - return itemSlot; - } - - @Override - public char getSellSlot() { - return sellSlot; - } - - @Override - public char getSellAllSlot() { - return sellAllSlot; - } - - @Override - public String[] getLayout() { - return layout; - } - - @Override - public String getTitle() { - return title; - } - - @Override - public double getEarningLimit(Player player) { - return new ExpressionBuilder( - PlaceholderManagerImpl.getInstance().parse( - player, - earningLimitExpression, - new HashMap<>() - )) - .build() - .evaluate(); - } - - public BuildableItem getSellIconLimitBuilder() { - return sellIconLimitBuilder; - } - - public BuildableItem getSellIconAllowBuilder() { - return sellIconAllowBuilder; - } - - public BuildableItem getSellIconDenyBuilder() { - return sellIconDenyBuilder; - } - - public BuildableItem getSellAllIconAllowBuilder() { - return sellAllIconAllowBuilder; - } - - public BuildableItem getSellAllIconDenyBuilder() { - return sellAllIconDenyBuilder; - } - - public BuildableItem getSellAllIconLimitBuilder() { - return sellAllIconLimitBuilder; - } - - public Action[] getSellDenyActions() { - return sellDenyActions; - } - - public Action[] getSellAllowActions() { - return sellAllowActions; - } - - public Action[] getSellLimitActions() { - return sellLimitActions; - } - - public Action[] getSellAllDenyActions() { - return sellAllDenyActions; - } - - public Action[] getSellAllAllowActions() { - return sellAllAllowActions; - } - - public Action[] getSellAllLimitActions() { - return sellAllLimitActions; - } - - @Override - public boolean isEnable() { - return enable; - } - - @Override - public boolean sellFishingBag() { - return sellFishingBag; - } - - @Override - public double getInventoryTotalWorth(Inventory inventory) { - double total = 0d; - for (ItemStack itemStack : inventory.getStorageContents()) { - double price = getItemPrice(itemStack); - total += price; - } - return total; - } - - @Override - public int getInventorySellAmount(Inventory inventory) { - int amount = 0; - for (ItemStack itemStack : inventory.getStorageContents()) { - double price = getItemPrice(itemStack); - if (price > 0 && itemStack != null) { - amount += itemStack.getAmount(); - } - } - return amount; - } - - public void clearWorthyItems(Inventory inventory) { - for (ItemStack itemStack : inventory.getStorageContents()) { - double price = getItemPrice(itemStack); - if (price > 0 && itemStack != null) { - itemStack.setAmount(0); - } - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/ChatCatcherManager.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/ChatCatcherManager.java deleted file mode 100644 index 2dcb9177..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/ChatCatcherManager.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.misc; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.gui.SectionPage; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.event.player.PlayerQuitEvent; - -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -public class ChatCatcherManager implements Listener { - - private final CustomFishingPlugin plugin; - private final ConcurrentHashMap> pageMap; - - public ChatCatcherManager(CustomFishingPlugin plugin) { - this.pageMap = new ConcurrentHashMap<>(); - this.plugin = plugin; - } - - public void load() { - Bukkit.getPluginManager().registerEvents(this, plugin); - } - - public void unload() { - this.pageMap.clear(); - HandlerList.unregisterAll(this); - } - - public void disable() { - unload(); - } - - public void catchMessage(Player player, String key, SectionPage page) { - this.pageMap.put(player.getUniqueId(), Pair.of(key, page)); - } - - @EventHandler - public void onQuit(PlayerQuitEvent event) { - pageMap.remove(event.getPlayer().getUniqueId()); - } - - @EventHandler - public void onChat(AsyncPlayerChatEvent event) { - var uuid = event.getPlayer().getUniqueId(); - var pair = pageMap.remove(uuid); - if (pair == null) return; - event.setCancelled(true); - plugin.getScheduler().runTaskSync(() -> { - pair.right().getSection().set(pair.left(), event.getMessage()); - pair.right().reOpen(); - }, event.getPlayer().getLocation()); - } -} 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 deleted file mode 100644 index 0f60dfd3..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/ConditionalElement.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.requirement; - -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.mechanic.misc.WeightModifier; -import net.momirealms.customfishing.api.mechanic.requirement.Requirement; -import org.bukkit.entity.Player; - -import java.util.HashMap; -import java.util.List; - -public class ConditionalElement { - - private final List> modifierList; - private final HashMap subLoots; - private final Requirement[] requirements; - - public ConditionalElement( - Requirement[] requirements, - List> modifierList, - HashMap subElements - ) { - this.modifierList = modifierList; - this.requirements = requirements; - 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); - weightMap.put(modifierPair.left(), modifierPair.right().modify(player, previous)); - } - } - - public Requirement[] getRequirements() { - return requirements; - } - - public HashMap getSubElements() { - return subLoots; - } -} 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 deleted file mode 100644 index 169cd2e1..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java +++ /dev/null @@ -1,1394 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.requirement; - -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.integration.LevelInterface; -import net.momirealms.customfishing.api.integration.SeasonInterface; -import net.momirealms.customfishing.api.manager.RequirementManager; -import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.competition.FishingCompetition; -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.mechanic.requirement.RequirementExpansion; -import net.momirealms.customfishing.api.mechanic.requirement.RequirementFactory; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.compatibility.VaultHook; -import net.momirealms.customfishing.compatibility.papi.ParseUtils; -import net.momirealms.customfishing.util.ClassUtils; -import net.momirealms.customfishing.util.ConfigUtils; -import net.momirealms.customfishing.util.MoonPhase; -import net.momirealms.sparrow.heart.SparrowHeart; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.MemorySection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -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; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.*; - -public class RequirementManagerImpl implements RequirementManager { - - public static Requirement[] mechanicRequirements; - private final CustomFishingPluginImpl plugin; - private final HashMap requirementFactoryMap; - private final LinkedHashMap conditionalLootsMap; - private final LinkedHashMap conditionalGamesMap; - private final String EXPANSION_FOLDER = "expansions/requirement"; - - public RequirementManagerImpl(CustomFishingPluginImpl plugin) { - this.plugin = plugin; - this.requirementFactoryMap = new HashMap<>(); - this.conditionalLootsMap = new LinkedHashMap<>(); - this.conditionalGamesMap = new LinkedHashMap<>(); - this.registerInbuiltRequirements(); - } - - public void load() { - this.loadExpansions(); - this.loadRequirementGroupFileConfig(); - } - - public void unload() { - this.conditionalLootsMap.clear(); - } - - public void disable() { - this.requirementFactoryMap.clear(); - this.conditionalLootsMap.clear(); - } - - @Override - public boolean putLegacyLootToMap(String key, Requirement[] requirements, double weight) { - if (conditionalLootsMap.containsKey("LEGACY_" + key)) { - return false; - } else { - conditionalLootsMap.put("LEGACY_" + key, new ConditionalElement(requirements, List.of(Pair.of(key, (player, origin) -> weight + origin)), new HashMap<>())); - return true; - } - } - - /** - * 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) { - conditionalLootsMap.put(entry.getKey(), getConditionalElements(section)); - } - } - - // 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) { - conditionalGamesMap.put(entry.getKey(), getConditionalElements(section)); - } - } - } - - /** - * 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.requirementFactoryMap.containsKey(type)) return false; - this.requirementFactoryMap.put(type, requirementFactory); - 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.requirementFactoryMap.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(); - this.registerContainRequirement(); - this.registerStartWithRequirement(); - this.registerEndWithRequirement(); - this.registerEqualsRequirement(); - this.registerBiomeRequirement(); - this.registerMoonPhaseRequirement(); - this.registerDateRequirement(); - this.registerPluginLevelRequirement(); - this.registerPermissionRequirement(); - this.registerWorldRequirement(); - this.registerWeatherRequirement(); - this.registerSeasonRequirement(); - this.registerLavaFishingRequirement(); - this.registerRodRequirement(); - this.registerBaitRequirement(); - this.registerGreaterThanRequirement(); - this.registerAndRequirement(); - this.registerOrRequirement(); - this.registerLevelRequirement(); - this.registerRandomRequirement(); - this.registerIceFishingRequirement(); - this.registerOpenWaterRequirement(); - this.registerCoolDownRequirement(); - this.registerGroupRequirement(); - this.registerLootRequirement(); - this.registerLessThanRequirement(); - this.registerNumberEqualRequirement(); - this.registerRegexRequirement(); - this.registerItemInHandRequirement(); - this.registerMoneyRequirement(); - this.registerInBagRequirement(); - this.registerHookRequirement(); - this.registerCompetitionRequirement(); - this.registerListRequirement(); - this.registerEnvironmentRequirement(); - this.registerPotionEffectRequirement(); - this.registerSizeRequirement(); - this.registerHasStatsRequirement(); - this.registerLootTypeRequirement(); - this.registerInListRequirement(); - } - - public HashMap getLootWithWeight(Condition condition) { - return getString2DoubleMap(condition, conditionalLootsMap); - } - - 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<>(); - Queue> lootQueue = new LinkedList<>(); - lootQueue.add(conditionalGamesMap); - Player player = condition.getPlayer(); - while (!lootQueue.isEmpty()) { - HashMap currentLootMap = lootQueue.poll(); - for (ConditionalElement loots : currentLootMap.values()) { - if (RequirementManager.isRequirementMet(condition, loots.getRequirements())) { - loots.combine(player, lootWeightMap); - if (loots.getSubElements() != null) { - lootQueue.add(loots.getSubElements()); - } - } - } - } - return lootWeightMap; - } - - /** - * 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) { - 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(getRequirement(typeOrName, entry.getValue())); - } else { - requirements.add(getRequirement(section.getConfigurationSection(typeOrName), advanced)); - } - } - return requirements.toArray(new Requirement[0]); - } - - @Override - public boolean hasRequirement(String type) { - return requirementFactoryMap.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) { - if (section == null) return EmptyRequirement.instance; - List actionList = null; - if (advanced) { - actionList = new ArrayList<>(); - if (section.contains("not-met-actions")) { - for (Map.Entry entry : Objects.requireNonNull(section.getConfigurationSection("not-met-actions")).getValues(false).entrySet()) { - if (entry.getValue() instanceof MemorySection inner) { - actionList.add(plugin.getActionManager().getAction(inner)); - } - } - } - } - String type = section.getString("type"); - if (type == null) { - LogUtils.warn("No requirement type found at " + section.getCurrentPath()); - return EmptyRequirement.instance; - } - 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 - @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 - @Nullable - public RequirementFactory getRequirementFactory(String type) { - return requirementFactoryMap.get(type); - } - - private void registerTimeRequirement() { - registerRequirement("time", (args, actions, advanced) -> { - List> timePairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); - return condition -> { - long time = condition.getLocation().getWorld().getTime(); - for (Pair pair : timePairs) - if (time >= pair.left() && time <= pair.right()) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerGroupRequirement() { - registerRequirement("group", (args, actions, advanced) -> { - HashSet arg = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - String lootID = condition.getArg("{loot}"); - if (lootID == null) return false; - Loot loot = plugin.getLootManager().getLoot(lootID); - if (loot == null) return false; - String[] groups = loot.getLootGroup(); - if (groups != null) { - for (String g : groups) { - if (arg.contains(g)) { - return true; - } - } - } - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("!group", (args, actions, advanced) -> { - HashSet arg = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - String lootID = condition.getArg("{loot}"); - Loot loot = plugin.getLootManager().getLoot(lootID); - String[] groups = loot.getLootGroup(); - if (groups == null) { - return true; - } - outer: { - for (String g : groups) { - if (arg.contains(g)) { - break outer; - } - } - return true; - } - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerLootRequirement() { - registerRequirement("loot", (args, actions, advanced) -> { - HashSet arg = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - String lootID = condition.getArg("{loot}"); - if (arg.contains(lootID)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("!loot", (args, actions, advanced) -> { - HashSet arg = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - String lootID = condition.getArg("{loot}"); - if (!arg.contains(lootID)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerYRequirement() { - registerRequirement("ypos", (args, actions, advanced) -> { - List> timePairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); - return condition -> { - int y = condition.getLocation().getBlockY(); - for (Pair pair : timePairs) - if (y >= pair.left() && y <= pair.right()) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerOrRequirement() { - registerRequirement("||", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - Requirement[] requirements = getRequirements(section, advanced); - return condition -> { - for (Requirement requirement : requirements) { - if (requirement.isConditionMet(condition)) { - return true; - } - } - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at || requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerAndRequirement() { - registerRequirement("&&", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - Requirement[] requirements = getRequirements(section, advanced); - return condition -> { - outer: { - for (Requirement requirement : requirements) { - if (!requirement.isConditionMet(condition)) { - break outer; - } - } - return true; - } - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at && requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerLavaFishingRequirement() { - registerRequirement("lava-fishing", (args, actions, advanced) -> { - boolean inLava = (boolean) args; - return condition -> { - String current = condition.getArgs().getOrDefault("{lava}","false"); - if (current.equals(String.valueOf(inLava))) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerOpenWaterRequirement() { - registerRequirement("open-water", (args, actions, advanced) -> { - boolean inLava = (boolean) args; - return condition -> { - String current = condition.getArgs().getOrDefault("{open-water}", "false"); - if (current.equals(String.valueOf(inLava))) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerIceFishingRequirement() { - registerRequirement("ice-fishing", (args, actions, advanced) -> { - boolean iceFishing = (boolean) args; - return condition -> { - Location location = condition.getLocation(); - int water = 0; - int ice = 0; - for (int i = -2; i <= 2; i++) { - for (int j = -1; j <= 2; j++) { - for (int k = -2; k <= 2; k++) { - Block block = location.clone().add(i, j, k).getBlock(); - Material material = block.getType(); - switch (material) { - case ICE -> ice++; - case WATER -> water++; - } - } - } - } - if ((ice >= 16 && water >= 25) == iceFishing) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerLevelRequirement() { - registerRequirement("level", (args, actions, advanced) -> { - int level = (int) args; - return condition -> { - int current = condition.getPlayer().getLevel(); - if (current >= level) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerMoneyRequirement() { - registerRequirement("money", (args, actions, advanced) -> { - double money = ConfigUtils.getDoubleValue(args); - return condition -> { - double current = VaultHook.getEconomy().getBalance(condition.getPlayer()); - if (current >= money) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerRandomRequirement() { - registerRequirement("random", (args, actions, advanced) -> { - double random = ConfigUtils.getDoubleValue(args); - return condition -> { - if (Math.random() < random) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerBiomeRequirement() { - registerRequirement("biome", (args, actions, advanced) -> { - HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(condition.getLocation()); - if (biomes.contains(currentBiome)) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("!biome", (args, actions, advanced) -> { - HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(condition.getLocation()); - if (!biomes.contains(currentBiome)) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerMoonPhaseRequirement() { - registerRequirement("moon-phase", (args, actions, advanced) -> { - HashSet moonPhases = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - long days = condition.getLocation().getWorld().getFullTime() / 24_000; - if (moonPhases.contains(MoonPhase.getPhase(days).name().toLowerCase(Locale.ENGLISH))) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("!moon-phase", (args, actions, advanced) -> { - HashSet moonPhases = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - long days = condition.getLocation().getWorld().getFullTime() / 24_000; - if (!moonPhases.contains(MoonPhase.getPhase(days).name().toLowerCase(Locale.ENGLISH))) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerWorldRequirement() { - registerRequirement("world", (args, actions, advanced) -> { - HashSet worlds = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - if (worlds.contains(condition.getLocation().getWorld().getName())) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("!world", (args, actions, advanced) -> { - HashSet worlds = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - if (!worlds.contains(condition.getLocation().getWorld().getName())) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerWeatherRequirement() { - registerRequirement("weather", (args, actions, advanced) -> { - List weathers = ConfigUtils.stringListArgs(args); - return condition -> { - String currentWeather; - World world = condition.getLocation().getWorld(); - if (world.isClearWeather()) currentWeather = "clear"; - else if (world.isThundering()) currentWeather = "thunder"; - else currentWeather = "rain"; - for (String weather : weathers) - if (weather.equalsIgnoreCase(currentWeather)) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerCoolDownRequirement() { - registerRequirement("cooldown", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String key = section.getString("key"); - int time = section.getInt("time"); - return condition -> { - if (!plugin.getCoolDownManager().isCoolDown(condition.getPlayer().getUniqueId(), key, time)) { - return true; - } - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at cooldown requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerDateRequirement() { - registerRequirement("date", (args, actions, advanced) -> { - HashSet dates = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - Calendar calendar = Calendar.getInstance(); - String current = (calendar.get(Calendar.MONTH) + 1) + "/" + calendar.get(Calendar.DATE); - if (dates.contains(current)) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerPermissionRequirement() { - registerRequirement("permission", (args, actions, advanced) -> { - List perms = ConfigUtils.stringListArgs(args); - return condition -> { - for (String perm : perms) - if (condition.getPlayer().hasPermission(perm)) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("!permission", (args, actions, advanced) -> { - List perms = ConfigUtils.stringListArgs(args); - return condition -> { - for (String perm : perms) - if (condition.getPlayer().hasPermission(perm)) { - if (advanced) triggerActions(actions, condition); - return false; - } - return true; - }; - }); - } - - private void registerSeasonRequirement() { - registerRequirement("season", (args, actions, advanced) -> { - List seasons = ConfigUtils.stringListArgs(args); - return condition -> { - SeasonInterface seasonInterface = plugin.getIntegrationManager().getSeasonInterface(); - if (seasonInterface == null) return true; - String season = seasonInterface.getSeason(condition.getLocation().getWorld()); - if (seasons.contains(season)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - @SuppressWarnings("DuplicatedCode") - private void registerGreaterThanRequirement() { - registerRequirement(">=", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) >= Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at >= requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement(">", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) > Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at > requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerRegexRequirement() { - registerRequirement("regex", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("papi", ""); - String v2 = section.getString("regex", ""); - return condition -> { - if (ParseUtils.setPlaceholders(condition.getPlayer(), v1).matches(v2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at regex requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerNumberEqualRequirement() { - registerRequirement("==", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) == Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at !startsWith requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!=", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) != Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at !startsWith requirement."); - return EmptyRequirement.instance; - } - }); - } - - @SuppressWarnings("DuplicatedCode") - private void registerLessThanRequirement() { - registerRequirement("<", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) < Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at < requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("<=", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (Double.parseDouble(p1) <= Double.parseDouble(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at <= requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerStartWithRequirement() { - registerRequirement("startsWith", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (p1.startsWith(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at startsWith requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!startsWith", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (!p1.startsWith(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at !startsWith requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerEndWithRequirement() { - registerRequirement("endsWith", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (p1.endsWith(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at endsWith requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!endsWith", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (!p1.endsWith(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at !endsWith requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerContainRequirement() { - registerRequirement("contains", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (p1.contains(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at contains requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!contains", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (!p1.contains(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at !contains requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerInListRequirement() { - registerRequirement("in-list", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String papi = section.getString("papi", ""); - List values = ConfigUtils.stringListArgs(section.get("values")); - return condition -> { - String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), papi) : papi; - if (values.contains(p1)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at in-list requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!in-list", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String papi = section.getString("papi", ""); - List values = ConfigUtils.stringListArgs(section.get("values")); - return condition -> { - String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), papi) : papi; - if (!values.contains(p1)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at in-list requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerEqualsRequirement() { - registerRequirement("equals", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (p1.equals(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at equals requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!equals", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(condition.getPlayer(), v2) : v2; - if (!p1.equals(p2)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at !equals requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerRodRequirement() { - registerRequirement("rod", (args, actions, advanced) -> { - List rods = ConfigUtils.stringListArgs(args); - return condition -> { - String id = condition.getArg("{rod}"); - if (rods.contains(id)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("!rod", (args, actions, advanced) -> { - List rods = ConfigUtils.stringListArgs(args); - return condition -> { - String id = condition.getArg("{rod}"); - if (!rods.contains(id)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerItemInHandRequirement() { - registerRequirement("item-in-hand", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - boolean mainOrOff = section.getString("hand","main").equalsIgnoreCase("main"); - int amount = section.getInt("amount", 1); - List items = ConfigUtils.stringListArgs(section.get("item")); - return condition -> { - ItemStack itemStack = mainOrOff ? - condition.getPlayer().getInventory().getItemInMainHand() - : condition.getPlayer().getInventory().getItemInOffHand(); - String id = plugin.getItemManager().getAnyPluginItemID(itemStack); - if (items.contains(id) && itemStack.getAmount() >= amount) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at item-in-hand requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerBaitRequirement() { - registerRequirement("bait", (args, actions, advanced) -> { - List baits = ConfigUtils.stringListArgs(args); - return condition -> { - String id = condition.getArg("{bait}"); - if (baits.contains(id)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("!bait", (args, actions, advanced) -> { - List baits = ConfigUtils.stringListArgs(args); - return condition -> { - String id = condition.getArg("{bait}"); - if (!baits.contains(id)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("has-bait", (args, actions, advanced) -> { - boolean has = (boolean) args; - return condition -> { - String id = condition.getArg("{bait}"); - if (id != null && has) return true; - if (id == null && !has) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerSizeRequirement() { - registerRequirement("has-size", (args, actions, advanced) -> { - boolean has = (boolean) args; - return condition -> { - String size = condition.getArg("{SIZE}"); - if (size != null && has) return true; - if (size == null && !has) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerHasStatsRequirement() { - registerRequirement("has-stats", (args, actions, advanced) -> { - boolean has = (boolean) args; - return condition -> { - String loot = condition.getArg("{loot}"); - Loot lootInstance = plugin.getLootManager().getLoot(loot); - if (lootInstance != null) { - if (!lootInstance.disableStats() && has) return true; - if (lootInstance.disableStats() && !has) return true; - } - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerLootTypeRequirement() { - registerRequirement("loot-type", (args, actions, advanced) -> { - List types = ConfigUtils.stringListArgs(args); - return condition -> { - String loot = condition.getArg("{loot}"); - Loot lootInstance = plugin.getLootManager().getLoot(loot); - if (lootInstance != null) { - if (types.contains(lootInstance.getType().name().toLowerCase(Locale.ENGLISH))) return true; - } - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("!loot-type", (args, actions, advanced) -> { - List types = ConfigUtils.stringListArgs(args); - return condition -> { - String loot = condition.getArg("{loot}"); - Loot lootInstance = plugin.getLootManager().getLoot(loot); - if (lootInstance != null) { - if (!types.contains(lootInstance.getType().name().toLowerCase(Locale.ENGLISH))) return true; - } - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerEnvironmentRequirement() { - registerRequirement("environment", (args, actions, advanced) -> { - List environments = ConfigUtils.stringListArgs(args); - return condition -> { - var name = condition.getLocation().getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); - if (environments.contains(name)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("!environment", (args, actions, advanced) -> { - List environments = ConfigUtils.stringListArgs(args); - return condition -> { - var name = condition.getLocation().getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); - if (!environments.contains(name)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerHookRequirement() { - registerRequirement("hook", (args, actions, advanced) -> { - List hooks = ConfigUtils.stringListArgs(args); - return condition -> { - String id = condition.getArg("{hook}"); - if (hooks.contains(id)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("!hook", (args, actions, advanced) -> { - List hooks = ConfigUtils.stringListArgs(args); - return condition -> { - String id = condition.getArg("{hook}"); - if (!hooks.contains(id)) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - registerRequirement("has-hook", (args, actions, advanced) -> { - boolean has = (boolean) args; - return condition -> { - String id = condition.getArg("{hook}"); - if (id != null && has) return true; - if (id == null && !has) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerInBagRequirement() { - registerRequirement("in-fishingbag", (args, actions, advanced) -> { - boolean arg = (boolean) args; - return condition -> { - String inBag = condition.getArg("{in-bag}"); - if (inBag == null && !arg) return true; - if (inBag != null && inBag.equals(String.valueOf(arg))) return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - private void registerListRequirement() { - registerRequirement("list", (args, actions, advanced) -> { - LogUtils.severe("It seems that you made a mistake where you put \"list\" into \"conditions\" section."); - ArrayList list = ConfigUtils.stringListArgs(args); - LogUtils.warn("list:"); - for (String e : list) { - LogUtils.warn(" - " + e); - } - return EmptyRequirement.instance; - }); - } - - private void registerCompetitionRequirement() { - registerRequirement("competition", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - boolean onCompetition = section.getBoolean("ongoing", true); - List ids = ConfigUtils.stringListArgs(section.get("id")); - return condition -> { - if (ids.size() == 0) { - if (plugin.getCompetitionManager().getOnGoingCompetition() != null == onCompetition) { - return true; - } - } else { - FishingCompetition competition = plugin.getCompetitionManager().getOnGoingCompetition(); - if (onCompetition) { - if (competition != null) { - if (ids.contains(competition.getConfig().getKey())) { - return true; - } - } - } else { - if (competition == null) { - return true; - } - } - } - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at competition requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerPluginLevelRequirement() { - registerRequirement("plugin-level", (args, actions, advanced) -> { - if (args instanceof ConfigurationSection section) { - String pluginName = section.getString("plugin"); - int level = section.getInt("level"); - String target = section.getString("target"); - return condition -> { - 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; - } - if (levelInterface.getLevel(condition.getPlayer(), target) >= level) - return true; - if (advanced) triggerActions(actions, condition); - return false; - }; - } else { - LogUtils.warn("Wrong value format found at plugin-level requirement."); - return EmptyRequirement.instance; - } - }); - } - - - private void registerPotionEffectRequirement() { - registerRequirement("potion-effect", (args, actions, advanced) -> { - String potions = (String) args; - String[] split = potions.split("(<=|>=|<|>|==)", 2); - PotionEffectType type = PotionEffectType.getByName(split[0]); - if (type == null) { - LogUtils.warn("Potion effect doesn't exist: " + split[0]); - return EmptyRequirement.instance; - } - int required = Integer.parseInt(split[1]); - String operator = potions.substring(split[0].length(), potions.length() - split[1].length()); - return condition -> { - int level = -1; - PotionEffect potionEffect = condition.getPlayer().getPotionEffect(type); - if (potionEffect != null) { - level = potionEffect.getAmplifier(); - } - boolean result = false; - switch (operator) { - case ">=" -> { - if (level >= required) result = true; - } - case ">" -> { - if (level > required) result = true; - } - case "==" -> { - if (level == required) result = true; - } - case "!=" -> { - if (level != required) result = true; - } - case "<=" -> { - if (level <= required) result = true; - } - case "<" -> { - if (level < required) result = true; - } - } - if (result) { - return true; - } - if (advanced) triggerActions(actions, condition); - return false; - }; - }); - } - - /** - * 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. - */ - @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, RequirementExpansion.class); - classes.add(expansionClass); - } catch (IOException | ClassNotFoundException e) { - LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); - } - } - } - try { - for (Class expansionClass : classes) { - RequirementExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); - unregisterRequirement(expansion.getRequirementType()); - registerRequirement(expansion.getRequirementType(), expansion.getRequirementFactory()); - LogUtils.info("Loaded requirement expansion: " + expansion.getRequirementType() + "[" + 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/totem/ActivatedTotem.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/ActivatedTotem.java deleted file mode 100644 index 35f70ee6..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/ActivatedTotem.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.totem; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.effect.EffectCarrier; -import net.momirealms.customfishing.api.mechanic.totem.TotemConfig; -import net.momirealms.customfishing.api.mechanic.totem.TotemParticle; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import org.bukkit.Location; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -public class ActivatedTotem { - - private final List subTasks; - private final Location coreLocation; - private final TotemConfig totemConfig; - private final long expireTime; - private final EffectCarrier effectCarrier; - - public ActivatedTotem(Location coreLocation, TotemConfig config) { - this.subTasks = new ArrayList<>(); - 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().getEffectCarrier("totem", config.getKey()); - for (TotemParticle particleSetting : config.getParticleSettings()) { - this.subTasks.add(particleSetting.start(coreLocation, config.getRadius())); - } - } - - public TotemConfig getTotemConfig() { - return totemConfig; - } - - public Location getCoreLocation() { - return coreLocation; - } - - public void cancel() { - for (CancellableTask task : subTasks) { - task.cancel(); - } - } - - public long getExpireTime() { - return expireTime; - } - - public void doTimerAction() { - HashMap args = new HashMap<>(); - args.put("{time_left}", String.valueOf((expireTime - System.currentTimeMillis())/1000)); - Condition condition = new Condition(coreLocation, null, args); - if (effectCarrier != null) { - Action[] actions = effectCarrier.getActions(ActionTrigger.TIMER); - if (actions != null) { - for (Action action : actions) { - action.trigger(condition); - } - } - } - } - - public EffectCarrier getEffectCarrier() { - return effectCarrier; - } -} 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 deleted file mode 100644 index 92c65703..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/totem/TotemManagerImpl.java +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.mechanic.totem; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.common.SimpleLocation; -import net.momirealms.customfishing.api.event.TotemActivateEvent; -import net.momirealms.customfishing.api.manager.TotemManager; -import net.momirealms.customfishing.api.mechanic.action.Action; -import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; -import net.momirealms.customfishing.api.mechanic.condition.Condition; -import net.momirealms.customfishing.api.mechanic.effect.EffectCarrier; -import net.momirealms.customfishing.api.mechanic.totem.TotemConfig; -import net.momirealms.customfishing.api.mechanic.totem.TotemModel; -import net.momirealms.customfishing.api.mechanic.totem.block.TotemBlock; -import net.momirealms.customfishing.api.mechanic.totem.block.property.AxisImpl; -import net.momirealms.customfishing.api.mechanic.totem.block.property.FaceImpl; -import net.momirealms.customfishing.api.mechanic.totem.block.property.HalfImpl; -import net.momirealms.customfishing.api.mechanic.totem.block.property.TotemBlockProperty; -import net.momirealms.customfishing.api.mechanic.totem.block.type.TypeCondition; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import net.momirealms.customfishing.mechanic.totem.particle.DustParticleSetting; -import net.momirealms.customfishing.mechanic.totem.particle.ParticleSetting; -import net.momirealms.customfishing.util.LocationUtils; -import org.bukkit.*; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.Bisected; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.event.Event; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.EquipmentSlot; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -public class TotemManagerImpl implements TotemManager, Listener { - - private final CustomFishingPlugin plugin; - private final HashMap> totemConfigMap; - private final List allMaterials; - private final ConcurrentHashMap activatedTotems; - private CancellableTask timerCheckTask; - - public TotemManagerImpl(CustomFishingPlugin plugin) { - this.plugin = plugin; - this.totemConfigMap = new HashMap<>(); - this.allMaterials = Arrays.stream(Material.values()).map(Enum::name).toList(); - this.activatedTotems = new ConcurrentHashMap<>(); - } - - public void load() { - this.loadConfig(); - Bukkit.getPluginManager().registerEvents(this, plugin); - this.timerCheckTask = plugin.getScheduler().runTaskAsyncTimer(() -> { - long time = System.currentTimeMillis(); - ArrayList removed = new ArrayList<>(); - for (Map.Entry entry : activatedTotems.entrySet()) { - if (time > entry.getValue().getExpireTime()) { - removed.add(entry.getKey()); - entry.getValue().cancel(); - } else { - entry.getValue().doTimerAction(); - } - } - for (SimpleLocation simpleLocation : removed) { - activatedTotems.remove(simpleLocation); - } - }, 1, 1, TimeUnit.SECONDS); - } - - public void unload() { - this.totemConfigMap.clear(); - for (ActivatedTotem activatedTotem : activatedTotems.values()) { - activatedTotem.cancel(); - } - activatedTotems.clear(); - HandlerList.unregisterAll(this); - if (this.timerCheckTask != null && !this.timerCheckTask.isCancelled()) - this.timerCheckTask.cancel(); - } - - public void disable() { - 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) { - for (ActivatedTotem activatedTotem : activatedTotems.values()) { - if (LocationUtils.getDistance(activatedTotem.getCoreLocation(), location) < activatedTotem.getTotemConfig().getRadius()) { - return activatedTotem.getEffectCarrier(); - } - } - return null; - } - - @EventHandler - public void onBreakTotemCore(BlockBreakEvent event) { - if (event.isCancelled()) - return; - Location location = event.getBlock().getLocation(); - SimpleLocation simpleLocation = SimpleLocation.getByBukkitLocation(location); - ActivatedTotem activatedTotem = activatedTotems.remove(simpleLocation); - if (activatedTotem != null) - activatedTotem.cancel(); - } - - @EventHandler (ignoreCancelled = true) - public void onInteractBlock(PlayerInteractEvent event) { - if (event.isBlockInHand()) - return; - if (event.getAction() != org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK) - return; - if (event.getHand() != EquipmentSlot.HAND) - return; - Block block = event.getClickedBlock(); - assert block != null; - String id = plugin.getBlockManager().getAnyPluginBlockID(block); - List configs = totemConfigMap.get(id); - if (configs == null) - return; - TotemConfig config = null; - for (TotemConfig temp : configs) { - if (temp.isRightPattern(block.getLocation())) { - config = temp; - break; - } - } - if (config == null) - return; - String totemKey = config.getKey(); - EffectCarrier carrier = plugin.getEffectManager().getEffectCarrier("totem", totemKey); - if (carrier == null) - return; - Condition condition = new Condition(block.getLocation(), event.getPlayer(), new HashMap<>()); - if (!carrier.isConditionMet(condition)) - return; - - TotemActivateEvent totemActivateEvent = new TotemActivateEvent(event.getPlayer(), block.getLocation(), config); - Bukkit.getPluginManager().callEvent(totemActivateEvent); - if (totemActivateEvent.isCancelled()) { - return; - } - - Action[] actions = carrier.getActionMap().get(ActionTrigger.ACTIVATE); - if (actions != null) - for (Action action : actions) { - action.trigger(condition); - } - Location location = block.getLocation(); - ActivatedTotem activatedTotem = new ActivatedTotem(location, config); - SimpleLocation simpleLocation = SimpleLocation.getByBukkitLocation(location); - - ActivatedTotem previous = this.activatedTotems.put(simpleLocation, activatedTotem); - if (previous != null) { - previous.cancel(); - } - } - - @SuppressWarnings("DuplicatedCode") - private void loadConfig() { - Deque fileDeque = new ArrayDeque<>(); - for (String type : List.of("totem")) { - File typeFolder = new File(plugin.getDataFolder() + File.separator + "contents" + File.separator + type); - if (!typeFolder.exists()) { - if (!typeFolder.mkdirs()) return; - plugin.saveResource("contents" + File.separator + type + File.separator + "default.yml", false); - } - fileDeque.push(typeFolder); - while (!fileDeque.isEmpty()) { - File file = fileDeque.pop(); - File[] files = file.listFiles(); - if (files == null) continue; - for (File subFile : files) { - if (subFile.isDirectory()) { - fileDeque.push(subFile); - } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { - this.loadSingleFile(subFile); - } - } - } - } - } - - private void loadSingleFile(File file) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - for (Map.Entry entry : config.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection section) { - TotemConfig totemConfig = new TotemConfig.Builder(entry.getKey()) - .setTotemModels(getTotemModels(section.getConfigurationSection("pattern"))) - .setParticleSettings(getParticleSettings(section.getConfigurationSection("particles"))) - .setRequirements(plugin.getRequirementManager().getRequirements(section.getConfigurationSection("requirements"), true)) - .setRadius(section.getDouble("radius", 8)) - .setDuration(section.getInt("duration", 300)) - .build(); - - HashSet coreMaterials = new HashSet<>(); - for (TotemBlock totemBlock : totemConfig.getTotemCore()) { - String text = totemBlock.getTypeCondition().getRawText(); - if (text.startsWith("*")) { - String sub = text.substring(1); - coreMaterials.addAll(allMaterials.stream().filter(it -> it.endsWith(sub)).toList()); - } else if (text.endsWith("*")) { - String sub = text.substring(0, text.length() - 1); - coreMaterials.addAll(allMaterials.stream().filter(it -> it.startsWith(sub)).toList()); - } else { - coreMaterials.add(text); - } - } - for (String material : coreMaterials) { - putTotemConfigToMap(material, totemConfig); - } - } - } - } - - private void putTotemConfigToMap(String material, TotemConfig totemConfig) { - List configs = this.totemConfigMap.getOrDefault(material, new ArrayList<>()); - configs.add(totemConfig); - this.totemConfigMap.put(material, configs); - } - - public ParticleSetting[] getParticleSettings(ConfigurationSection section) { - List particleSettings = new ArrayList<>(); - if (section != null) - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - particleSettings.add(getParticleSetting(innerSection)); - } - } - return particleSettings.toArray(new ParticleSetting[0]); - } - - public ParticleSetting getParticleSetting(ConfigurationSection section) { - Particle particle = Particle.valueOf(section.getString("type","REDSTONE")); - String formulaHorizontal = section.getString("polar-coordinates-formula.horizontal"); - String formulaVertical = section.getString("polar-coordinates-formula.vertical"); - List> ranges = section.getStringList("theta.range") - .stream().map(it -> { - String[] split = it.split("~"); - return Pair.of(Double.parseDouble(split[0]) * Math.PI / 180, Double.parseDouble(split[1]) * Math.PI / 180); - }).toList(); - - double interval = section.getDouble("theta.draw-interval", 3d); - int delay = section.getInt("task.delay", 0); - int period = section.getInt("task.period", 0); - if (particle == Particle.REDSTONE) { - String color = section.getString("options.color","0,0,0"); - String[] colorSplit = color.split(","); - return new DustParticleSetting( - formulaHorizontal, - formulaVertical, - particle, - interval, - ranges, - delay, - period, - new Particle.DustOptions( - Color.fromRGB( - Integer.parseInt(colorSplit[0]), - Integer.parseInt(colorSplit[1]), - Integer.parseInt(colorSplit[2]) - ), - (float) section.getDouble("options.scale", 1) - ) - ); - } else if (particle == Particle.DUST_COLOR_TRANSITION) { - String color = section.getString("options.from","0,0,0"); - String[] colorSplit = color.split(","); - String toColor = section.getString("options.to","255,255,255"); - String[] toColorSplit = toColor.split(","); - return new DustParticleSetting( - formulaHorizontal, - formulaVertical, - particle, - interval, - ranges, - delay, - period, - new Particle.DustTransition( - Color.fromRGB( - Integer.parseInt(colorSplit[0]), - Integer.parseInt(colorSplit[1]), - Integer.parseInt(colorSplit[2]) - ), - Color.fromRGB( - Integer.parseInt(toColorSplit[0]), - Integer.parseInt(toColorSplit[1]), - Integer.parseInt(toColorSplit[2]) - ), - (float) section.getDouble("options.scale", 1) - ) - ); - } else { - return new ParticleSetting( - formulaHorizontal, - formulaVertical, - particle, - interval, - ranges, - delay, - period - ); - } - } - - public TotemModel[] getTotemModels(ConfigurationSection section) { - TotemModel originalModel = parseModel(section); - List modelList = new ArrayList<>(); - for (int i = 0; i < 4; i++) { - originalModel = originalModel.deepClone().rotate90(); - modelList.add(originalModel); - if (i % 2 == 0) { - modelList.add(originalModel.mirrorVertically()); - } else { - modelList.add(originalModel.mirrorHorizontally()); - } - } - return modelList.toArray(new TotemModel[0]); - } - - @SuppressWarnings("unchecked") - public TotemModel parseModel(ConfigurationSection section) { - ConfigurationSection layerSection = section.getConfigurationSection("layer"); - List totemBlocksList = new ArrayList<>(); - if (layerSection != null) { - var set = layerSection.getValues(false).entrySet(); - TotemBlock[][][][] totemBlocks = new TotemBlock[set.size()][][][]; - for (Map.Entry entry : set) { - if (entry.getValue() instanceof List list) { - totemBlocks[Integer.parseInt(entry.getKey())-1] = parseLayer((List) list); - } - } - totemBlocksList.addAll(List.of(totemBlocks)); - } - - String[] core = section.getString("core","1,1,1").split(","); - int x = Integer.parseInt(core[2]) - 1; - int z = Integer.parseInt(core[1]) - 1; - int y = Integer.parseInt(core[0]) - 1; - return new TotemModel( - x,y,z, - totemBlocksList.toArray(new TotemBlock[0][][][]) - ); - } - - public TotemBlock[][][] parseLayer(List lines) { - List totemBlocksList = new ArrayList<>(); - for (String line : lines) { - totemBlocksList.add(parseSingleLine(line)); - } - return totemBlocksList.toArray(new TotemBlock[0][][]); - } - - public TotemBlock[][] parseSingleLine(String line) { - List totemBlocksList = new ArrayList<>(); - String[] splits = line.split("\\s+"); - for (String split : splits) { - totemBlocksList.add(parseSingleElement(split)); - } - return totemBlocksList.toArray(new TotemBlock[0][]); - } - - public TotemBlock[] parseSingleElement(String element) { - String[] orBlocks = element.split("\\|\\|"); - List totemBlockList = new ArrayList<>(); - for (String block : orBlocks) { - int index = block.indexOf("{"); - List propertyList = new ArrayList<>(); - if (index == -1) { - index = block.length(); - } else { - String propertyStr = block.substring(index+1, block.length()-1); - String[] properties = propertyStr.split(";"); - for (String property : properties) { - String[] split = property.split("="); - if (split.length < 2) continue; - String key = split[0]; - String value = split[1]; - switch (key) { - // Block face - case "face" -> { - BlockFace blockFace = BlockFace.valueOf(value.toUpperCase(Locale.ENGLISH)); - propertyList.add(new FaceImpl(blockFace)); - } - // Block axis - case "axis" -> { - Axis axis = Axis.valueOf(value.toUpperCase(Locale.ENGLISH)); - propertyList.add(new AxisImpl(axis)); - } - // Slab, Stair half - case "half" -> { - Bisected.Half half = Bisected.Half.valueOf(value.toUpperCase(Locale.ENGLISH)); - propertyList.add(new HalfImpl(half)); - } - } - } - } - String type = block.substring(0, index); - TotemBlock totemBlock = new TotemBlock( - TypeCondition.getTypeCondition(type), - propertyList.toArray(new TotemBlockProperty[0]) - ); - totemBlockList.add(totemBlock); - } - return totemBlockList.toArray(new TotemBlock[0]); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/scheduler/BukkitSchedulerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/scheduler/BukkitSchedulerImpl.java deleted file mode 100644 index b3de4a61..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/scheduler/BukkitSchedulerImpl.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.scheduler; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -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; - - public BukkitSchedulerImpl(CustomFishingPlugin plugin) { - 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()) - runnable.run(); - else - 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) { - if (delay == 0) { - if (Bukkit.isPrimaryThread()) runnable.run(); - else Bukkit.getScheduler().runTask(plugin, runnable); - return new BukkitCancellableTask(null); - } - 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; - - public BukkitCancellableTask(BukkitTask bukkitTask) { - this.bukkitTask = bukkitTask; - } - - @Override - public void cancel() { - if (this.bukkitTask != null) - this.bukkitTask.cancel(); - } - - @Override - public boolean isCancelled() { - if (this.bukkitTask == null) return true; - return this.bukkitTask.isCancelled(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/scheduler/FoliaSchedulerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/scheduler/FoliaSchedulerImpl.java deleted file mode 100644 index 838e5f28..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/scheduler/FoliaSchedulerImpl.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.scheduler; - -import io.papermc.paper.threadedregions.scheduler.ScheduledTask; -import net.momirealms.customfishing.api.CustomFishingPlugin; -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; - - public FoliaSchedulerImpl(CustomFishingPlugin plugin) { - 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) { - if (location == null) { - Bukkit.getGlobalRegionScheduler().execute(plugin, runnable); - } else { - 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) { - if (location == null) { - return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().runAtFixedRate(plugin, (scheduledTask -> runnable.run()), delay, 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) { - if (delay == 0) { - if (location == null) { - return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().run(plugin, (scheduledTask -> runnable.run()))); - } - return new FoliaCancellableTask(Bukkit.getRegionScheduler().run(plugin, location, (scheduledTask -> runnable.run()))); - } - if (location == null) { - return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().runDelayed(plugin, (scheduledTask -> runnable.run()), 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; - - public FoliaCancellableTask(ScheduledTask scheduledTask) { - this.scheduledTask = scheduledTask; - } - - @Override - public void cancel() { - this.scheduledTask.cancel(); - } - - @Override - public boolean isCancelled() { - return this.scheduledTask.isCancelled(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/scheduler/SchedulerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/scheduler/SchedulerImpl.java deleted file mode 100644 index 267ec3bc..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/scheduler/SchedulerImpl.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.scheduler; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import net.momirealms.customfishing.api.scheduler.Scheduler; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.setting.CFConfig; -import org.bukkit.Location; - -import java.util.concurrent.ScheduledFuture; -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; - private final ScheduledThreadPoolExecutor schedule; - private final CustomFishingPlugin plugin; - - public SchedulerImpl(CustomFishingPlugin plugin) { - this.plugin = plugin; - this.syncScheduler = plugin.getVersionManager().hasRegionScheduler() ? - new FoliaSchedulerImpl(plugin) : new BukkitSchedulerImpl(plugin); - this.schedule = new ScheduledThreadPoolExecutor(1); - this.schedule.setMaximumPoolSize(1); - this.schedule.setKeepAliveTime(30, TimeUnit.SECONDS); - this.schedule.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); - } - - /** - * Reloads the scheduler configuration based on CustomFishingPlugin settings. - */ - public void reload() { - try { - this.schedule.setMaximumPoolSize(CFConfig.maximumPoolSize); - this.schedule.setCorePoolSize(CFConfig.corePoolSize); - this.schedule.setKeepAliveTime(CFConfig.keepAliveTime, TimeUnit.SECONDS); - } catch (IllegalArgumentException e) { - LogUtils.warn("Failed to create thread pool. Please lower the corePoolSize in config.yml.", e); - } - } - - /** - * 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) { - try { - this.schedule.execute(runnable); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * 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(() -> { - try { - runnable.run(); - } catch (Exception e) { - e.printStackTrace(); - } - }, 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(() -> { - runTaskSync(runnable, location); - }, 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(() -> { - try { - runnable.run(); - } catch (Exception e) { - e.printStackTrace(); - } - }, delay, period, timeUnit)); - } - - /** - * Represents a thread-pool task that can be cancelled. - */ - public static class ScheduledTask implements CancellableTask { - - private final ScheduledFuture scheduledFuture; - - public ScheduledTask(ScheduledFuture scheduledFuture) { - this.scheduledFuture = scheduledFuture; - } - - @Override - public void cancel() { - this.scheduledFuture.cancel(false); - } - - @Override - public boolean isCancelled() { - return this.scheduledFuture.isCancelled(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/scheduler/SyncScheduler.java b/plugin/src/main/java/net/momirealms/customfishing/scheduler/SyncScheduler.java deleted file mode 100644 index 9d706dc1..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/scheduler/SyncScheduler.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.scheduler; - -import net.momirealms.customfishing.api.scheduler.CancellableTask; -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); - - /** - * 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); - - /** - * 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/setting/CFConfig.java b/plugin/src/main/java/net/momirealms/customfishing/setting/CFConfig.java deleted file mode 100644 index 8fa8282f..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/setting/CFConfig.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.setting; - -import dev.dejvokep.boostedyaml.YamlDocument; -import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; -import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; -import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; -import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; -import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.api.util.OffsetUtils; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.event.EventPriority; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Objects; - -public class CFConfig { - - // config version - public static String configVersion = "32"; - // Debug mode - public static boolean debug; - // language - public static String language; - - // update checker - public static boolean updateChecker; - - // BStats - public static boolean metrics; - - // fishing event priority - public static EventPriority eventPriority; - - // thread pool settings - public static int corePoolSize; - public static int maximumPoolSize; - public static int keepAliveTime; - - // detection order for item id - public static List itemDetectOrder = new ArrayList<>(); - public static List blockDetectOrder = new ArrayList<>(); - - // fishing bag - public static boolean enableFishingBag; - - // Fishing wait time - public static boolean overrideVanilla; - public static int waterMinTime; - public static int waterMaxTime; - // Lava fishing - public static int lavaMinTime; - public static int lavaMaxTime; - - // Competition - public static boolean redisRanking; - public static String serverGroup; - public static int placeholderLimit; - - // Data save interval - public static int dataSaveInterval; - // Lock data on join - public static boolean lockData; - public static boolean logDataSaving; - - public static boolean restrictedSizeRange; - - // Legacy color code support - public static boolean legacyColorSupport; - // Durability lore - public static List durabilityLore; - - public static boolean globalShowInFinder; - public static boolean globalDisableStats; - public static boolean globalDisableGame; - public static boolean globalInstantGame; - - public static int multipleLootSpawnDelay; - - public static void load() { - try { - YamlDocument.create( - new File(CustomFishingPlugin.getInstance().getDataFolder(), "config.yml"), - Objects.requireNonNull(CustomFishingPlugin.getInstance().getResource("config.yml")), - GeneralSettings.DEFAULT, - LoaderSettings - .builder() - .setAutoUpdate(true) - .build(), - DumperSettings.DEFAULT, - UpdaterSettings - .builder() - .setVersioning(new BasicVersioning("config-version")) - .addIgnoredRoute(configVersion, "mechanics.mechanic-requirements", '.') - .addIgnoredRoute(configVersion, "mechanics.global-events", '.') - .addIgnoredRoute(configVersion, "mechanics.global-effects", '.') - .addIgnoredRoute(configVersion, "mechanics.fishing-bag.collect-actions", '.') - .addIgnoredRoute(configVersion, "mechanics.fishing-bag.full-actions", '.') - .addIgnoredRoute(configVersion, "other-settings.placeholder-register", '.') - .build() - ); - loadSettings(CustomFishingPlugin.getInstance().getConfig("config.yml")); - } catch (IOException e) { - LogUtils.warn(e.getMessage()); - } - } - - private static void loadSettings(YamlConfiguration config) { - debug = config.getBoolean("debug", false); - - language = config.getString("lang", "english"); - updateChecker = config.getBoolean("update-checker"); - metrics = config.getBoolean("metrics"); - eventPriority = EventPriority.valueOf(config.getString("other-settings.event-priority", "NORMAL").toUpperCase(Locale.ENGLISH)); - - corePoolSize = config.getInt("other-settings.thread-pool-settings.corePoolSize", 1); - maximumPoolSize = config.getInt("other-settings.thread-pool-settings.maximumPoolSize", 1); - keepAliveTime = config.getInt("other-settings.thread-pool-settings.keepAliveTime", 30); - - itemDetectOrder = config.getStringList("other-settings.item-detection-order"); - blockDetectOrder = config.getStringList("other-settings.block-detection-order"); - - enableFishingBag = config.getBoolean("mechanics.fishing-bag.enable", true); - - overrideVanilla = config.getBoolean("mechanics.fishing-wait-time.override-vanilla", false); - waterMinTime = config.getInt("mechanics.fishing-wait-time.min-wait-time", 100); - waterMaxTime = config.getInt("mechanics.fishing-wait-time.min-wait-time", 600); - - lavaMinTime = config.getInt("mechanics.lava-fishing.min-wait-time", 100); - lavaMaxTime = config.getInt("mechanics.lava-fishing.max-wait-time", 600); - - restrictedSizeRange = config.getBoolean("mechanics.size.restricted-size-range", true); - - globalShowInFinder = config.getBoolean("mechanics.global-loot-property.show-in-fishfinder", true); - globalDisableStats = config.getBoolean("mechanics.global-loot-property.disable-stat", false); - globalDisableGame = config.getBoolean("mechanics.global-loot-property.disable-game", false); - globalInstantGame = config.getBoolean("mechanics.global-loot-property.instant-game", false); - - redisRanking = config.getBoolean("mechanics.competition.redis-ranking", false); - placeholderLimit = config.getInt("mechanics.competition.placeholder-limit", 3); - serverGroup = config.getString("mechanics.competition.server-group","default"); - - multipleLootSpawnDelay = config.getInt("mechanics.multiple-loot-spawn-delay", 0); - - dataSaveInterval = config.getInt("other-settings.data-saving-interval", 600); - logDataSaving = config.getBoolean("other-settings.log-data-saving", true); - lockData = config.getBoolean("other-settings.lock-data", true); - legacyColorSupport = config.getBoolean("other-settings.legacy-color-code-support", false); - - durabilityLore = config.getStringList("other-settings.custom-durability-format").stream().map(it -> "" + it).toList(); - - OffsetUtils.loadConfig(config.getConfigurationSection("other-settings.offset-characters")); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/setting/CFLocale.java b/plugin/src/main/java/net/momirealms/customfishing/setting/CFLocale.java deleted file mode 100644 index 0eb42ca7..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/setting/CFLocale.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.setting; - -import dev.dejvokep.boostedyaml.YamlDocument; -import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; -import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; -import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; -import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; -import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.util.LogUtils; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -public class CFLocale { - public static String MSG_Total_Size; - public static String MSG_Catch_Amount; - public static String MSG_Total_Score; - public static String MSG_Max_Size; - public static String MSG_No_Player; - public static String MSG_No_Score; - public static String MSG_Prefix; - public static String MSG_Reload; - public static String MSG_Competition_Not_Exist; - public static String MSG_No_Competition_Ongoing; - public static String MSG_End_Competition; - public static String MSG_Stop_Competition; - public static String MSG_No_Rank; - public static String MSG_Item_Not_Exists; - public static String MSG_Get_Item; - public static String MSG_Give_Item; - public static String MSG_Never_Played; - public static String MSG_Unsafe_Modification; - public static String MSG_Data_Not_Loaded; - public static String MSG_Market_GUI_Open; - public static String MSG_Fishing_Bag_Open; - public static String MSG_Split_Char; - public static String MSG_Possible_Loots; - public static String FORMAT_Day; - public static String FORMAT_Hour; - public static String FORMAT_Minute; - public static String FORMAT_Second; - public static String GUI_SCROLL_DOWN; - public static String GUI_SCROLL_UP; - public static String GUI_CANNOT_SCROLL_UP; - public static String GUI_CANNOT_SCROLL_DOWN; - public static String GUI_NEXT_PAGE; - public static String GUI_GOTO_NEXT_PAGE; - public static String GUI_CANNOT_GOTO_NEXT_PAGE; - public static String GUI_PREVIOUS_PAGE; - public static String GUI_GOTO_PREVIOUS_PAGE; - public static String GUI_CANNOT_GOTO_PREVIOUS_PAGE; - public static String GUI_BACK_TO_PARENT_PAGE; - public static String GUI_BACK_TO_PARENT_FOLDER; - public static String GUI_CURRENT_VALUE; - public static String GUI_CLICK_TO_TOGGLE; - public static String GUI_LEFT_CLICK_EDIT; - public static String GUI_RIGHT_CLICK_RESET; - public static String GUI_RIGHT_CLICK_DELETE; - public static String GUI_LOOT_SHOW_IN_FINDER; - public static String GUI_LOOT_SCORE; - public static String GUI_LOOT_NICK; - public static String GUI_LOOT_INSTANT_GAME; - public static String GUI_LOOT_DISABLE_STATS; - public static String GUI_LOOT_DISABLE_GAME; - public static String GUI_ITEM_AMOUNT; - public static String GUI_ITEM_MODEL_DATA; - public static String GUI_ITEM_DISPLAY_NAME; - public static String GUI_ITEM_DURABILITY; - public static String GUI_ITEM_ENCHANTMENT; - public static String GUI_ITEM_HEAD64; - public static String GUI_ITEM_FLAG; - public static String GUI_ITEM_LORE; - public static String GUI_ITEM_MATERIAL; - public static String GUI_ITEM_NBT; - public static String GUI_ITEM_PREVENT_GRAB; - public static String GUI_ITEM_PRICE; - public static String GUI_ITEM_PRICE_BASE; - public static String GUI_ITEM_PRICE_BONUS; - public static String GUI_ITEM_RANDOM_DURABILITY; - public static String GUI_ITEM_SIZE; - public static String GUI_ITEM_STACKABLE; - public static String GUI_ITEM_STORED_ENCHANTMENT; - public static String GUI_ITEM_TAG; - public static String GUI_ITEM_UNBREAKABLE; - public static String GUI_DELETE_PROPERTY; - public static String GUI_NEW_VALUE; - public static String GUI_CLICK_CONFIRM; - public static String GUI_INVALID_NUMBER; - public static String GUI_ILLEGAL_FORMAT; - public static String GUI_TITLE_AMOUNT; - public static String GUI_TITLE_MODEL_DATA; - public static String GUI_TITLE_DISPLAY_NAME; - public static String GUI_NEW_DISPLAY_NAME; - public static String GUI_TITLE_CUSTOM_DURABILITY; - public static String GUI_TITLE_ENCHANTMENT; - public static String GUI_TITLE_STORED_ENCHANTMENT; - public static String GUI_SELECT_ONE_ENCHANTMENT; - public static String GUI_ADD_NEW_ENCHANTMENT; - public static String GUI_TITLE_ITEM_FLAG; - public static String GUI_TITLE_LORE; - public static String GUI_ADD_NEW_LORE; - public static String GUI_SELECT_ONE_LORE; - public static String GUI_TITLE_MATERIAL; - public static String GUI_TITLE_NBT_COMPOUND; - public static String GUI_TITLE_NBT_LIST; - public static String GUI_TITLE_NBT_KEY; - public static String GUI_NBT_INVALID_KEY; - public static String GUI_RIGHT_CLICK_CANCEL; - public static String GUI_NBT_ADD_COMPOUND; - public static String GUI_NBT_ADD_LIST; - public static String GUI_NBT_ADD_VALUE; - public static String GUI_NBT_PREVIEW; - public static String GUI_NBT_BACK_TO_COMPOUND; - public static String GUI_NBT_SET_VALUE_TITLE; - public static String GUI_NBT_EDIT_TITLE; - public static String GUI_NICK_TITLE; - public static String GUI_NICK_NEW; - public static String GUI_PRICE_TITLE; - public static String GUI_PRICE_BASE; - public static String GUI_PRICE_BONUS; - public static String GUI_SCORE_TITLE; - public static String GUI_SIZE_TITLE; - public static String GUI_SIZE_MIN; - public static String GUI_SIZE_MAX; - public static String GUI_SIZE_MAX_NO_LESS; - public static String GUI_SELECT_FILE; - public static String GUI_SELECT_ITEM; - public static String GUI_ADD_NEW_KEY; - public static String GUI_DUPE_INVALID_KEY; - public static String GUI_SEARCH; - public static String GUI_TEMP_NEW_KEY; - public static String GUI_SET_NEW_KEY; - public static String GUI_EDIT_KEY; - - public static void load() { - InputStream inputStream = CustomFishingPlugin.getInstance().getResource("messages/" + CFConfig.language + ".yml"); - if (inputStream != null) { - try { - YamlDocument.create( - new File(CustomFishingPlugin.getInstance().getDataFolder(), "messages/" + CFConfig.language + ".yml"), - inputStream, - GeneralSettings.DEFAULT, - LoaderSettings - .builder() - .setAutoUpdate(true) - .build(), - DumperSettings.DEFAULT, - UpdaterSettings - .builder() - .setVersioning(new BasicVersioning("config-version")) - .build() - ); - inputStream.close(); - } catch (IOException e) { - LogUtils.warn(e.getMessage()); - } - } - loadSettings(CustomFishingPlugin.get().getConfig("messages/" + CFConfig.language + ".yml")); - } - - private static void loadSettings(YamlConfiguration locale) { - ConfigurationSection msgSection = locale.getConfigurationSection("messages"); - if (msgSection != null) { - MSG_Prefix = msgSection.getString("prefix"); - MSG_Reload = msgSection.getString("reload"); - MSG_Competition_Not_Exist = msgSection.getString("competition-not-exist"); - MSG_No_Competition_Ongoing = msgSection.getString("no-competition-ongoing"); - MSG_Stop_Competition = msgSection.getString("stop-competition"); - MSG_End_Competition = msgSection.getString("end-competition"); - MSG_No_Player = msgSection.getString("no-player"); - MSG_No_Score = msgSection.getString("no-score"); - MSG_No_Rank = msgSection.getString("no-rank"); - MSG_Catch_Amount = msgSection.getString("goal-catch-amount"); - MSG_Max_Size = msgSection.getString("goal-max-size"); - MSG_Total_Score = msgSection.getString("goal-total-score"); - MSG_Total_Size = msgSection.getString("goal-total-size"); - MSG_Item_Not_Exists = msgSection.getString("item-not-exist"); - MSG_Get_Item = msgSection.getString("get-item"); - MSG_Give_Item = msgSection.getString("give-item"); - MSG_Never_Played = msgSection.getString("never-played"); - MSG_Unsafe_Modification = msgSection.getString("unsafe-modification"); - MSG_Data_Not_Loaded = msgSection.getString("data-not-loaded"); - MSG_Market_GUI_Open = msgSection.getString("open-market-gui"); - MSG_Fishing_Bag_Open = msgSection.getString("open-fishing-bag"); - MSG_Split_Char = msgSection.getString("split-char"); - MSG_Possible_Loots = msgSection.getString("possible-loots"); - FORMAT_Day = msgSection.getString("format-day"); - FORMAT_Hour = msgSection.getString("format-hour"); - FORMAT_Minute = msgSection.getString("format-minute"); - FORMAT_Second = msgSection.getString("format-second"); - } - ConfigurationSection guiSection = locale.getConfigurationSection("gui"); - if (guiSection != null) { - GUI_SEARCH = guiSection.getString("search"); - GUI_EDIT_KEY = guiSection.getString("edit-key"); - GUI_DELETE_PROPERTY = guiSection.getString("delete-property"); - GUI_DUPE_INVALID_KEY = guiSection.getString("dupe-invalid-key"); - GUI_SELECT_ITEM = guiSection.getString("select-item"); - GUI_SELECT_FILE = guiSection.getString("select-file"); - GUI_TEMP_NEW_KEY = guiSection.getString("temp-new-key"); - GUI_SET_NEW_KEY = guiSection.getString("set-new-key"); - GUI_ADD_NEW_KEY = guiSection.getString("page-add-new-key"); - GUI_SCROLL_UP = guiSection.getString("scroll-up"); - GUI_SCROLL_DOWN = guiSection.getString("scroll-down"); - GUI_CANNOT_SCROLL_UP = guiSection.getString("cannot-scroll-up"); - GUI_CANNOT_SCROLL_DOWN = guiSection.getString("cannot-scroll-down"); - GUI_NEXT_PAGE = guiSection.getString("next-page"); - GUI_GOTO_NEXT_PAGE = guiSection.getString("goto-next-page"); - GUI_CANNOT_GOTO_NEXT_PAGE = guiSection.getString("cannot-goto-next-page"); - GUI_PREVIOUS_PAGE = guiSection.getString("previous-page"); - GUI_GOTO_PREVIOUS_PAGE = guiSection.getString("goto-previous-page"); - GUI_CANNOT_GOTO_PREVIOUS_PAGE = guiSection.getString("cannot-goto-previous-page"); - GUI_BACK_TO_PARENT_PAGE = guiSection.getString("back-to-parent-page"); - GUI_BACK_TO_PARENT_FOLDER = guiSection.getString("back-to-parent-folder"); - GUI_CURRENT_VALUE = guiSection.getString("current-value"); - GUI_CLICK_TO_TOGGLE = guiSection.getString("click-to-toggle"); - GUI_LEFT_CLICK_EDIT = guiSection.getString("left-click-edit"); - GUI_RIGHT_CLICK_RESET = guiSection.getString("right-click-reset"); - GUI_RIGHT_CLICK_DELETE = guiSection.getString("right-click-delete"); - GUI_RIGHT_CLICK_CANCEL = guiSection.getString("right-click-cancel"); - GUI_LOOT_SHOW_IN_FINDER = guiSection.getString("loot-show-in-finder"); - GUI_LOOT_SCORE = guiSection.getString("loot-score"); - GUI_LOOT_NICK = guiSection.getString("loot-nick"); - GUI_LOOT_INSTANT_GAME = guiSection.getString("loot-instant-game"); - GUI_LOOT_DISABLE_STATS = guiSection.getString("loot-disable-statistics"); - GUI_LOOT_DISABLE_GAME = guiSection.getString("loot-disable-game"); - GUI_ITEM_AMOUNT = guiSection.getString("item-amount"); - GUI_ITEM_MODEL_DATA = guiSection.getString("item-custom-model-data"); - GUI_ITEM_DISPLAY_NAME = guiSection.getString("item-display-name"); - GUI_ITEM_DURABILITY = guiSection.getString("item-custom-durability"); - GUI_ITEM_ENCHANTMENT = guiSection.getString("item-enchantment"); - GUI_ITEM_HEAD64 = guiSection.getString("item-head64"); - GUI_ITEM_FLAG = guiSection.getString("item-item-flag"); - GUI_ITEM_LORE = guiSection.getString("item-lore"); - GUI_ITEM_MATERIAL = guiSection.getString("item-material"); - GUI_ITEM_NBT = guiSection.getString("item-nbt"); - GUI_ITEM_PREVENT_GRAB = guiSection.getString("item-prevent-grab"); - GUI_ITEM_PRICE = guiSection.getString("item-price"); - GUI_ITEM_PRICE_BASE = guiSection.getString("item-price-base"); - GUI_ITEM_PRICE_BONUS = guiSection.getString("item-price-bonus"); - GUI_ITEM_RANDOM_DURABILITY = guiSection.getString("item-random-durability"); - GUI_ITEM_SIZE = guiSection.getString("item-size"); - GUI_ITEM_STACKABLE = guiSection.getString("item-stackable"); - GUI_ITEM_STORED_ENCHANTMENT = guiSection.getString("item-stored-enchantment"); - GUI_ITEM_TAG = guiSection.getString("item-tag"); - GUI_ITEM_UNBREAKABLE = guiSection.getString("item-unbreakable"); - GUI_NEW_VALUE = guiSection.getString("new-value"); - GUI_CLICK_CONFIRM = guiSection.getString("click-confirm"); - GUI_INVALID_NUMBER = guiSection.getString("invalid-number"); - GUI_ILLEGAL_FORMAT = guiSection.getString("illegal-format"); - GUI_TITLE_AMOUNT = guiSection.getString("page-amount-title"); - GUI_TITLE_MODEL_DATA = guiSection.getString("page-model-data-title"); - GUI_TITLE_DISPLAY_NAME = guiSection.getString("page-display-name-title"); - GUI_NEW_DISPLAY_NAME = guiSection.getString("page-new-display-name"); - GUI_TITLE_CUSTOM_DURABILITY = guiSection.getString("page-custom-durability-title"); - GUI_TITLE_ENCHANTMENT = guiSection.getString("page-enchantment-title"); - GUI_TITLE_STORED_ENCHANTMENT = guiSection.getString("page-stored-enchantment-title"); - GUI_SELECT_ONE_ENCHANTMENT = guiSection.getString("page-select-one-enchantment"); - GUI_ADD_NEW_ENCHANTMENT = guiSection.getString("page-add-new-enchantment"); - GUI_TITLE_ITEM_FLAG = guiSection.getString("page-item-flag-title"); - GUI_TITLE_LORE = guiSection.getString("page-lore-title"); - GUI_ADD_NEW_LORE = guiSection.getString("page-add-new-lore"); - GUI_SELECT_ONE_LORE = guiSection.getString("page-select-one-lore"); - GUI_TITLE_MATERIAL = guiSection.getString("page-material-title"); - GUI_TITLE_NBT_COMPOUND = guiSection.getString("page-nbt-compound-key-title"); - GUI_TITLE_NBT_LIST = guiSection.getString("page-nbt-list-key-title"); - GUI_TITLE_NBT_KEY = guiSection.getString("page-nbt-key-title"); - GUI_NBT_INVALID_KEY = guiSection.getString("page-nbt-invalid-key"); - GUI_NBT_ADD_COMPOUND = guiSection.getString("page-nbt-add-new-compound"); - GUI_NBT_ADD_LIST = guiSection.getString("page-nbt-add-new-list"); - GUI_NBT_ADD_VALUE = guiSection.getString("page-nbt-add-new-value"); - GUI_NBT_PREVIEW = guiSection.getString("page-nbt-preview"); - GUI_NBT_BACK_TO_COMPOUND = guiSection.getString("page-nbt-back-to-compound"); - GUI_NBT_SET_VALUE_TITLE = guiSection.getString("page-nbt-set-value-title"); - GUI_NBT_EDIT_TITLE = guiSection.getString("page-nbt-edit-title"); - GUI_NICK_TITLE = guiSection.getString("page-nick-title"); - GUI_NICK_NEW = guiSection.getString("page-new-nick"); - GUI_PRICE_TITLE = guiSection.getString("page-price-title"); - GUI_PRICE_BASE = guiSection.getString("page-base-price"); - GUI_PRICE_BONUS = guiSection.getString("page-base-bonus"); - GUI_SCORE_TITLE = guiSection.getString("page-score-title"); - GUI_SIZE_TITLE = guiSection.getString("page-size-title"); - GUI_SIZE_MIN = guiSection.getString("page-size-min"); - GUI_SIZE_MAX = guiSection.getString("page-size-max"); - GUI_SIZE_MAX_NO_LESS = guiSection.getString("page-size-max-no-less-min"); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/StorageManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/StorageManagerImpl.java deleted file mode 100644 index d9fbbd6d..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/StorageManagerImpl.java +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.storage; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonSyntaxException; -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.DataStorageInterface; -import net.momirealms.customfishing.api.data.PlayerData; -import net.momirealms.customfishing.api.data.StorageType; -import net.momirealms.customfishing.api.data.user.OfflineUser; -import net.momirealms.customfishing.api.data.user.OnlineUser; -import net.momirealms.customfishing.api.manager.StorageManager; -import net.momirealms.customfishing.api.scheduler.CancellableTask; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.setting.CFConfig; -import net.momirealms.customfishing.storage.method.database.nosql.MongoDBImpl; -import net.momirealms.customfishing.storage.method.database.nosql.RedisManager; -import net.momirealms.customfishing.storage.method.database.sql.H2Impl; -import net.momirealms.customfishing.storage.method.database.sql.MariaDBImpl; -import net.momirealms.customfishing.storage.method.database.sql.MySQLImpl; -import net.momirealms.customfishing.storage.method.database.sql.SQLiteImpl; -import net.momirealms.customfishing.storage.method.file.JsonImpl; -import net.momirealms.customfishing.storage.method.file.YAMLImpl; -import net.momirealms.customfishing.storage.user.OfflineUserImpl; -import net.momirealms.customfishing.storage.user.OnlineUserImpl; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; -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; - private DataStorageInterface dataSource; - private StorageType previousType; - private final ConcurrentHashMap onlineUserMap; - private final HashSet locked; - private boolean hasRedis; - private RedisManager redisManager; - private String uniqueID; - private CancellableTask timerSaveTask; - private final Gson gson; - - public StorageManagerImpl(CustomFishingPluginImpl plugin) { - this.plugin = plugin; - this.locked = new HashSet<>(); - this.onlineUserMap = new ConcurrentHashMap<>(); - this.gson = new GsonBuilder().create(); - 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(); - this.previousType = storageType; - switch (storageType) { - case H2 -> this.dataSource = new H2Impl(plugin); - case JSON -> this.dataSource = new JsonImpl(plugin); - case YAML -> this.dataSource = new YAMLImpl(plugin); - case SQLite -> this.dataSource = new SQLiteImpl(plugin); - case MySQL -> this.dataSource = new MySQLImpl(plugin); - case MariaDB -> this.dataSource = new MariaDBImpl(plugin); - case MongoDB -> this.dataSource = new MongoDBImpl(plugin); - } - 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(); - } - - // 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(); - this.dataSource.updateManyPlayersData(this.onlineUserMap.values(), !CFConfig.lockData); - if (CFConfig.logDataSaving) - LogUtils.info("Data Saved for online players. Took " + (System.currentTimeMillis() - time1) + "ms."); - }, - CFConfig.dataSaveInterval, - CFConfig.dataSaveInterval, - TimeUnit.SECONDS - ); - } - - /** - * Disables the storage manager and cleans up resources. - */ - public void disable() { - HandlerList.unregisterAll(this); - this.dataSource.updateManyPlayersData(onlineUserMap.values(), true); - this.onlineUserMap.clear(); - if (this.dataSource != null) - this.dataSource.disable(); - if (this.redisManager != null) - 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); - } - - @Override - public Collection getOnlineUsers() { - return onlineUserMap.values(); - } - - /** - * 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); - return optionalDataFuture.thenCompose(optionalUser -> { - if (optionalUser.isEmpty()) { - // locked - return CompletableFuture.completedFuture(Optional.empty()); - } - PlayerData data = optionalUser.get(); - if (data.isLocked()) { - return CompletableFuture.completedFuture(Optional.of(OfflineUserImpl.LOCKED_USER)); - } else { - OfflineUser offlineUser = new OfflineUserImpl(uuid, data.getName(), data); - return CompletableFuture.completedFuture(Optional.of(offlineUser)); - } - }); - } - - @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); - } - - /** - * 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(); - UUID uuid = player.getUniqueId(); - locked.add(uuid); - if (!hasRedis) { - waitForDataLockRelease(uuid, 1); - } else { - 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(); - UUID uuid = player.getUniqueId(); - if (locked.contains(uuid)) - return; - - OnlineUser onlineUser = onlineUserMap.remove(uuid); - if (onlineUser == null) return; - PlayerData data = onlineUser.getPlayerData(); - - if (hasRedis) { - redisManager.setChangeServer(uuid).thenRun( - () -> redisManager.updatePlayerData(uuid, data, true).thenRun( - () -> dataSource.updatePlayerData(uuid, data, true).thenAccept( - result -> { - if (result) locked.remove(uuid); - }))); - } else { - dataSource.updatePlayerData(uuid, data, true).thenAccept( - result -> { - if (result) locked.remove(uuid); - }); - } - } - - /** - * 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; - private int triedTimes; - private final CancellableTask task; - - public RedisGetDataTask(UUID uuid) { - this.uuid = uuid; - this.task = plugin.getScheduler().runTaskAsyncTimer(this, 0, 333, TimeUnit.MILLISECONDS); - } - - @Override - public void run() { - triedTimes++; - Player player = Bukkit.getPlayer(uuid); - if (player == null || !player.isOnline()) { - // offline - task.cancel(); - return; - } - if (triedTimes >= 6) { - waitForDataLockRelease(uuid, 3); - return; - } - redisManager.getPlayerData(uuid, false).thenAccept(optionalData -> { - if (optionalData.isPresent()) { - putDataInCache(player, optionalData.get()); - task.cancel(); - if (CFConfig.lockData) dataSource.lockOrUnlockPlayerData(uuid, true); - } - }); - } - } - - /** - * 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()) - return; - if (times > 3) { - LogUtils.warn("Tried 3 times when getting data for " + uuid + ". Giving up."); - return; - } - this.dataSource.getPlayerData(uuid, CFConfig.lockData).thenAccept(optionalData -> { - // Data should not be empty - if (optionalData.isEmpty()) { - LogUtils.severe("Unexpected error: Data is null"); - return; - } - - if (optionalData.get().isLocked()) { - waitForDataLockRelease(uuid, times + 1); - } else { - try { - putDataInCache(player, optionalData.get()); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - }, 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) { - try { - return gson.fromJson(json, PlayerData.class); - } catch (JsonSyntaxException e) { - LogUtils.severe("Failed to parse PlayerData from json"); - LogUtils.info("Json: " + json); - throw new RuntimeException(e); - } - } - - /** - * Converts bytes to PlayerData. - * - * @param data The byte array to be converted. - * @return The PlayerData object. - */ - @Override - @NotNull - public PlayerData fromBytes(byte[] data) { - return fromJson(new String(data, 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 deleted file mode 100644 index 29f242b2..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractHikariDatabase.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.storage.method.database.sql; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.*; -import net.momirealms.customfishing.api.util.LogUtils; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -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; - private final String driverClass; - private final String sqlBrand; - - public AbstractHikariDatabase(CustomFishingPlugin plugin) { - super(plugin); - this.driverClass = getStorageType() == StorageType.MariaDB ? "org.mariadb.jdbc.Driver" : "com.mysql.cj.jdbc.Driver"; - this.sqlBrand = getStorageType() == StorageType.MariaDB ? "MariaDB" : "MySQL"; - try { - Class.forName(this.driverClass); - } catch (ClassNotFoundException e1) { - if (getStorageType() == StorageType.MariaDB) { - LogUtils.warn("No MariaDB driver is found"); - } else if (getStorageType() == StorageType.MySQL) { - try { - Class.forName("com.mysql.jdbc.Driver"); - } catch (ClassNotFoundException e2) { - LogUtils.warn("No MySQL driver is found"); - } - } - } - } - - /** - * Initialize the database connection pool and create tables if they don't exist. - */ - @Override - public void initialize() { - YamlConfiguration config = plugin.getConfig("database.yml"); - ConfigurationSection section = config.getConfigurationSection(sqlBrand); - - if (section == null) { - LogUtils.warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one."); - return; - } - - super.tablePrefix = section.getString("table-prefix", "customfishing"); - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setUsername(section.getString("user", "root")); - hikariConfig.setPassword(section.getString("password", "pa55w0rd")); - hikariConfig.setJdbcUrl(String.format("jdbc:%s://%s:%s/%s%s", - sqlBrand.toLowerCase(Locale.ENGLISH), - section.getString("host", "localhost"), - section.getString("port", "3306"), - section.getString("database", "minecraft"), - section.getString("connection-parameters") - )); - hikariConfig.setDriverClassName(driverClass); - hikariConfig.setMaximumPoolSize(section.getInt("Pool-Settings.max-pool-size", 10)); - hikariConfig.setMinimumIdle(section.getInt("Pool-Settings.min-idle", 10)); - hikariConfig.setMaxLifetime(section.getLong("Pool-Settings.max-lifetime", 180000L)); - hikariConfig.setConnectionTimeout(section.getLong("Pool-Settings.time-out", 20000L)); - hikariConfig.setPoolName("CustomFishingHikariPool"); - try { - hikariConfig.setKeepaliveTime(section.getLong("Pool-Settings.keep-alive-time", 60000L)); - } catch (NoSuchMethodError ignored) { - } - - final Properties properties = new Properties(); - properties.putAll( - Map.of("cachePrepStmts", "true", - "prepStmtCacheSize", "250", - "prepStmtCacheSqlLimit", "2048", - "useServerPrepStmts", "true", - "useLocalSessionState", "true", - "useLocalTransactionState", "true" - )); - properties.putAll( - Map.of( - "rewriteBatchedStatements", "true", - "cacheResultSetMetadata", "true", - "cacheServerConfiguration", "true", - "elideSetAutoCommits", "true", - "maintainTimeStats", "false") - ); - hikariConfig.setDataSourceProperties(properties); - dataSource = new HikariDataSource(hikariConfig); - 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>(); - plugin.getScheduler().runTaskAsync(() -> { - try ( - Connection connection = getConnection() - ) { - var builder = new PlayerData.Builder().setName(""); - PreparedStatement statementOne = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("fishingbag"))); - statementOne.setString(1, uuid.toString()); - ResultSet rsOne = statementOne.executeQuery(); - if (rsOne.next()) { - int size = rsOne.getInt("size"); - String contents = rsOne.getString("contents"); - builder.setBagData(new InventoryData(contents, size)); - } else { - builder.setBagData(InventoryData.empty()); - } - - PreparedStatement statementTwo = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("selldata"))); - statementTwo.setString(1, uuid.toString()); - ResultSet rsTwo = statementTwo.executeQuery(); - if (rsTwo.next()) { - int date = rsTwo.getInt("date"); - double money = rsTwo.getInt("money"); - builder.setEarningData(new EarningData(money, date)); - } else { - builder.setEarningData(EarningData.empty()); - } - - PreparedStatement statementThree = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("statistics"))); - statementThree.setString(1, uuid.toString()); - ResultSet rsThree = statementThree.executeQuery(); - if (rsThree.next()) { - String stats = rsThree.getString("stats"); - var amountMap = (Map) Arrays.stream(stats.split(";")) - .map(element -> element.split(":")) - .filter(pair -> pair.length == 2) - .collect(Collectors.toMap(pair -> pair[0], pair -> Integer.parseInt(pair[1]))); - builder.setStats(new StatisticData(amountMap, new HashMap<>())); - } else { - builder.setStats(StatisticData.empty()); - } - future.complete(Optional.of(builder.build())); - } catch (SQLException e) { - LogUtils.warn("Failed to get " + uuid + "'s data.", e); - future.completeExceptionally(e); - } - }); - return future; - } -} 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 deleted file mode 100644 index e3ac1db4..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/YAMLImpl.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.storage.method.file; - -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.*; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.storage.method.AbstractStorage; -import net.momirealms.customfishing.util.ConfigUtils; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -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") - public YAMLImpl(CustomFishingPlugin plugin) { - super(plugin); - File folder = new File(plugin.getDataFolder(), "data"); - if (!folder.exists()) folder.mkdirs(); - } - - @Override - public StorageType getStorageType() { - 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"); - } - - @Override - public CompletableFuture> getPlayerData(UUID uuid, boolean lock) { - File dataFile = getPlayerDataFile(uuid); - if (!dataFile.exists()) { - if (Bukkit.getPlayer(uuid) != null) { - return CompletableFuture.completedFuture(Optional.of(PlayerData.empty())); - } else { - return CompletableFuture.completedFuture(Optional.empty()); - } - } - YamlConfiguration data = ConfigUtils.readData(dataFile); - - PlayerData playerData = new PlayerData.Builder() - .setBagData(new InventoryData(data.getString("bag", ""), data.getInt("size", 9))) - .setEarningData(new EarningData(data.getDouble("earnings"), data.getInt("date"))) - .setStats(getStatistics(data.getConfigurationSection("stats"))) - .setName(data.getString("name")) - .build(); - return CompletableFuture.completedFuture(Optional.of(playerData)); - } - - @Override - public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData, boolean ignore) { - YamlConfiguration data = new YamlConfiguration(); - data.set("name", playerData.getName()); - data.set("bag", playerData.getBagData().serialized); - data.set("size", playerData.getBagData().size); - data.set("date", playerData.getEarningData().date); - data.set("earnings", playerData.getEarningData().earnings); - ConfigurationSection section = data.createSection("stats"); - ConfigurationSection amountSection = section.createSection("amount"); - ConfigurationSection sizeSection = section.createSection("size"); - for (Map.Entry entry : playerData.getStatistics().amountMap.entrySet()) { - amountSection.set(entry.getKey(), entry.getValue()); - } - for (Map.Entry entry : playerData.getStatistics().sizeMap.entrySet()) { - sizeSection.set(entry.getKey(), entry.getValue()); - } - try { - data.save(getPlayerDataFile(uuid)); - } catch (IOException e) { - LogUtils.warn("Failed to save player data", e); - } - return CompletableFuture.completedFuture(true); - } - - @Override - public Set getUniqueUsers(boolean legacy) { - File folder; - if (legacy) { - folder = new File(plugin.getDataFolder(), "data/fishingbag"); - } else { - folder = new File(plugin.getDataFolder(), "data"); - } - Set uuids = new HashSet<>(); - if (folder.exists()) { - File[] files = folder.listFiles(); - if (files != null) { - for (File file : files) { - uuids.add(UUID.fromString(file.getName().substring(0, file.getName().length() - 4))); - } - } - } - return uuids; - } - - /** - * Parse statistics data from a YAML ConfigurationSection. - * - * @param section The ConfigurationSection containing statistics data. - * @return The parsed StatisticData object. - */ - private StatisticData getStatistics(ConfigurationSection section) { - HashMap amountMap = new HashMap<>(); - HashMap sizeMap = new HashMap<>(); - if (section == null) { - return new StatisticData(amountMap, sizeMap); - } - ConfigurationSection amountSection = section.getConfigurationSection("amount"); - if (amountSection != null) { - for (Map.Entry entry : amountSection.getValues(false).entrySet()) { - amountMap.put(entry.getKey(), (Integer) entry.getValue()); - } - } - ConfigurationSection sizeSection = section.getConfigurationSection("size"); - if (sizeSection != null) { - for (Map.Entry entry : sizeSection.getValues(false).entrySet()) { - sizeMap.put(entry.getKey(), ((Double) entry.getValue()).floatValue()); - } - } - return new StatisticData(amountMap, sizeMap); - } - - @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()) { - YamlConfiguration yaml = YamlConfiguration.loadConfiguration(bagFile); - String contents = yaml.getString("contents", ""); - int size = yaml.getInt("size", 9); - builder.setBagData(new InventoryData(contents, size)); - } else { - builder.setBagData(InventoryData.empty()); - } - - File statFile = new File(plugin.getDataFolder(), "data/statistics/" + uuid + ".yml"); - if (statFile.exists()) { - YamlConfiguration yaml = YamlConfiguration.loadConfiguration(statFile); - HashMap map = new HashMap<>(); - for (Map.Entry entry : yaml.getValues(false).entrySet()) { - if (entry.getValue() instanceof Integer integer) { - map.put(entry.getKey(), integer); - } - } - builder.setStats(new StatisticData(map, new HashMap<>())); - } else { - builder.setStats(StatisticData.empty()); - } - - File sellFile = new File(plugin.getDataFolder(), "data/sell/" + uuid + ".yml"); - if (sellFile.exists()) { - YamlConfiguration yaml = YamlConfiguration.loadConfiguration(sellFile); - builder.setEarningData(new EarningData(yaml.getDouble("earnings"), yaml.getInt("date"))); - } else { - builder.setEarningData(EarningData.empty()); - } - - return CompletableFuture.completedFuture(Optional.of(builder.build())); - } -} 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 deleted file mode 100644 index 066a2ef4..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/user/OfflineUserImpl.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.storage.user; - -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.data.EarningData; -import net.momirealms.customfishing.api.data.InventoryData; -import net.momirealms.customfishing.api.data.PlayerData; -import net.momirealms.customfishing.api.data.StatisticData; -import net.momirealms.customfishing.api.data.user.OfflineUser; -import net.momirealms.customfishing.api.mechanic.bag.FishingBagHolder; -import net.momirealms.customfishing.api.mechanic.statistic.Statistics; -import net.momirealms.customfishing.api.util.InventoryUtils; -import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; - -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; - private final String name; - private final FishingBagHolder holder; - private final EarningData earningData; - private final Statistics statistics; - 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, - AdventureHelper.getInstance().getComponentFromMiniMessage( - PlaceholderManagerImpl.getInstance().parse( - offlinePlayer, - CustomFishingPlugin.get().getBagManager().getBagTitle(), - Map.of("{player}", Optional.ofNullable(offlinePlayer.getName()).orElse(String.valueOf(uuid))) - ) - ))); - this.holder.setItems(InventoryUtils.getInventoryItems(playerData.getBagData().serialized)); - this.earningData = playerData.getEarningData(); - int date = CustomFishingPlugin.get().getMarketManager().getDate(); - if (earningData.date != date) { - earningData.date = date; - earningData.earnings = 0d; - } - this.statistics = new Statistics(playerData.getStatistics()); - } - - @Override - public String getName() { - return name; - } - - @Override - public UUID getUUID() { - return uuid; - } - - @Override - public FishingBagHolder getHolder() { - return holder; - } - - @Override - public EarningData getEarningData() { - return earningData; - } - - @Override - public Statistics getStatistics() { - return statistics; - } - - @Override - public boolean isOnline() { - Player player = Bukkit.getPlayer(uuid); - return player != null && player.isOnline(); - } - - @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) - .setStats(new StatisticData(statistics.getStatisticMap(), statistics.getSizeMap())) - .setName(name) - .build(); - } -} 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 deleted file mode 100644 index ea8f63d1..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/user/OnlineUserImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.storage.user; - -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; - } - - @Override - public Player getPlayer() { - return 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 deleted file mode 100644 index df9d4ab0..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ArmorStandUtils.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.util; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.wrappers.*; -import com.google.common.collect.Lists; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.momirealms.customfishing.CustomFishingPluginImpl; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import org.bukkit.Location; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -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 { - - private 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); - try { - entityPacket.getModifier().write(0, id); - entityPacket.getModifier().write(1, UUID.randomUUID()); - entityPacket.getEntityTypeModifier().write(0, EntityType.ARMOR_STAND); - entityPacket.getDoubles().write(0, location.getX()); - entityPacket.getDoubles().write(1, location.getY()); - entityPacket.getDoubles().write(2, location.getZ()); - if (CustomFishingPlugin.get().getVersionManager().isVersionNewerThan1_19()) { - entityPacket.getBytes().write(0, (byte) ((location.getYaw() % 360) * 128 / 180)); - } else { - entityPacket.getIntegers().write(5, (int) ((location.getYaw() % 360) * 128 / 180)); - } - } catch (Exception e) { - e.printStackTrace(); - } - 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); - if (CustomFishingPlugin.get().getVersionManager().isVersionNewerThan1_19_R2()) { - WrappedDataWatcher wrappedDataWatcher = createDataWatcher(); - setValueList(metaPacket, wrappedDataWatcher); - } else { - metaPacket.getWatchableCollectionModifier().write(0, createDataWatcher().getWatchableObjects()); - } - 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 -> { - final WrappedDataWatcher.WrappedDataWatcherObject dataWatcherObject = entry.getWatcherObject(); - wrappedDataValueList.add(new WrappedDataValue(dataWatcherObject.getIndex(), dataWatcherObject.getSerializer(), entry.getRawValue())); - }); - 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); - if (CustomFishingPlugin.get().getVersionManager().isVersionNewerThan1_19_R2()) { - WrappedDataWatcher wrappedDataWatcher = createDataWatcher(component); - setValueList(metaPacket, wrappedDataWatcher); - } else { - metaPacket.getWatchableCollectionModifier().write(0, createDataWatcher(component).getWatchableObjects()); - } - 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); - WrappedDataWatcher.Serializer serializer2 = WrappedDataWatcher.Registry.get(Byte.class); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(3, serializer1), false); - byte flag = 0x20; - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(0, serializer2), flag); - 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); - WrappedDataWatcher.Serializer serializer2 = WrappedDataWatcher.Registry.get(Byte.class); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(2, WrappedDataWatcher.Registry.getChatComponentSerializer(true)), Optional.of(WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(component)).getHandle())); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(3, serializer1), true); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(15, serializer2), (byte) 0x01); - byte flag = 0x20; - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(0, serializer2), flag); - 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); - List> pairs = new ArrayList<>(); - pairs.add(new Pair<>(EnumWrappers.ItemSlot.HEAD, itemStack)); - equipPacket.getSlotStackPairLists().write(0, pairs); - return equipPacket; - } - - /** - * 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); - if (CustomFishingPlugin.get().getVersionManager().isVersionNewerThan1_19_R3()) { - CustomFishingPluginImpl.sendPackets(player, getSpawnPacket(id, location.clone().subtract(0,1,0)), getMetaPacket(id), getEquipPacket(id, itemStack)); - } else { - CustomFishingPluginImpl.sendPacket(player, getSpawnPacket(id, location.clone().subtract(0,1,0))); - CustomFishingPluginImpl.sendPacket(player, getMetaPacket(id)); - CustomFishingPluginImpl.sendPacket(player, getEquipPacket(id, itemStack)); - } - CustomFishingPlugin.get().getScheduler().runTaskAsyncLater(() -> CustomFishingPluginImpl.getProtocolManager().sendServerPacket(player, getDestroyPacket(id)), seconds * 50L, TimeUnit.MILLISECONDS); - } - - /** - * 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); - if (CustomFishingPlugin.get().getVersionManager().isVersionNewerThan1_19_R3()) { - CustomFishingPluginImpl.sendPackets(player, getSpawnPacket(id, location.clone().subtract(0,1,0)), getMetaPacket(id, component)); - } else { - CustomFishingPluginImpl.sendPacket(player, getSpawnPacket(id, location.clone().subtract(0,1,0))); - CustomFishingPluginImpl.sendPacket(player, getMetaPacket(id, component)); - } - 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/ConfigUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/ConfigUtils.java deleted file mode 100644 index b3f4b513..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ConfigUtils.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.util; - -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.common.Tuple; -import net.momirealms.customfishing.api.mechanic.misc.Value; -import net.momirealms.customfishing.api.mechanic.misc.WeightModifier; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; -import net.momirealms.customfishing.mechanic.misc.value.ExpressionValue; -import net.momirealms.customfishing.mechanic.misc.value.PlainValue; -import net.objecthunter.exp4j.ExpressionBuilder; -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.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * Utility class for configuration-related operations. - */ -public class ConfigUtils { - - private 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<>(); - if (object instanceof String member) { - list.add(member); - } else if (object instanceof List members) { - list.addAll((Collection) members); - } else if (object instanceof String[] strings) { - list.addAll(List.of(strings)); - } - 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 regex) { - String[] split = value.split(regex); - 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) { - String[] split = member.split(":",2); - String key = split[0]; - result.add(Pair.of(key, Double.parseDouble(split[1]))); - } - 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; - } else if (arg instanceof Integer i) { - return Double.valueOf(i); - } - return 0; - } - - /** - * Converts an object into an integer value. - * - * @param arg The input object - * @return An integer value - */ - public static int getIntegerValue(Object arg) { - if (arg instanceof Integer i) { - return i; - } else if (arg instanceof Double d) { - return d.intValue(); - } - return 0; - } - - /** - * Converts an object into a "value". - * - * @param arg int / double / expression - * @return Value - */ - public static Value getValue(Object arg) { - if (arg instanceof Integer i) { - return new PlainValue(i); - } else if (arg instanceof Double d) { - return new PlainValue(d); - } else if (arg instanceof String s) { - return new ExpressionValue(s); - } - throw new IllegalArgumentException("Illegal value type"); - } - - /** - * Parses a string representing a size range and returns a pair of floats. - * - * @param string The size string in the format "min~max". - * @return A pair of floats representing the minimum and maximum size. - */ - @Nullable - public static Pair getFloatPair(String string) { - if (string == null) return null; - String[] split = string.split("~", 2); - if (split.length != 2) { - LogUtils.warn("Illegal size argument: " + string); - LogUtils.warn("Correct usage example: 10.5~25.6"); - throw new IllegalArgumentException("Illegal float range"); - } - return Pair.of(Float.parseFloat(split[0]), Float.parseFloat(split[1])); - } - - /** - * Parses a string representing a size range and returns a pair of ints. - * - * @param string The size string in the format "min~max". - * @return A pair of ints representing the minimum and maximum size. - */ - @Nullable - public static Pair getIntegerPair(String string) { - if (string == null) return null; - String[] split = string.split("~", 2); - if (split.length != 2) { - LogUtils.warn("Illegal size argument: " + string); - LogUtils.warn("Correct usage example: 10~20"); - throw new IllegalArgumentException("Illegal int range"); - } - 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 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) { - String[] split = member.split(":",2); - String key = split[0]; - result.add(Pair.of(key, getModifier(split[1]))); - } - return result; - } - - /** - * Retrieves a list of enchantment pairs from a configuration section. - * - * @param section The configuration section to extract enchantment data from. - * @return A list of enchantment pairs. - */ - @NotNull - public static List> getEnchantmentPair(ConfigurationSection section) { - List> list = new ArrayList<>(); - if (section == null) return list; - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof Integer integer) { - list.add(Pair.of(entry.getKey(), Short.parseShort(String.valueOf(Math.max(1, Math.min(Short.MAX_VALUE, integer)))))); - } - } - return list; - } - - public static List> getEnchantAmountPair(ConfigurationSection section) { - List> list = new ArrayList<>(); - if (section == null) return list; - for (Map.Entry entry : section.getValues(false).entrySet()) { - list.add(Pair.of(Integer.parseInt(entry.getKey()), getValue(entry.getValue()))); - } - return list; - } - - public static List, Value>> getEnchantPoolPair(ConfigurationSection section) { - List, Value>> list = new ArrayList<>(); - if (section == null) return list; - for (Map.Entry entry : section.getValues(false).entrySet()) { - list.add(Pair.of(getEnchantmentPair(entry.getKey()), getValue(entry.getValue()))); - } - return list; - } - - public static Pair getEnchantmentPair(String value) { - int last = value.lastIndexOf(":"); - if (last == -1 || last == 0 || last == value.length() - 1) { - throw new IllegalArgumentException("Invalid format of the input enchantment"); - } - return Pair.of(value.substring(0, last), Short.parseShort(value.substring(last + 1))); - } - - /** - * Retrieves a list of enchantment tuples from a configuration section. - * - * @param section The configuration section to extract enchantment data from. - * @return A list of enchantment tuples. - */ - @NotNull - public static List> getEnchantmentTuple(ConfigurationSection section) { - List> list = new ArrayList<>(); - if (section == null) return list; - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - Tuple tuple = Tuple.of( - inner.getDouble("chance"), - inner.getString("enchant"), - Short.valueOf(String.valueOf(inner.getInt("level"))) - ); - list.add(tuple); - } - } - return list; - } - - public static String getString(Object o) { - if (o instanceof String s) { - return s; - } else if (o instanceof Integer i) { - return String.valueOf(i); - } else if (o instanceof Double d) { - return String.valueOf(d); - } - throw new IllegalArgumentException("Illegal string format: " + o); - } - - /** - * 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) { - if (!file.exists()) { - try { - file.getParentFile().mkdirs(); - file.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - LogUtils.warn("Failed to generate data files!"); - } - } - 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."); - } - switch (text.charAt(0)) { - case '/' -> { - double arg = Double.parseDouble(text.substring(1)); - return (player, weight) -> weight / arg; - } - case '*' -> { - double arg = Double.parseDouble(text.substring(1)); - return (player, weight) -> weight * arg; - } - case '-' -> { - double arg = Double.parseDouble(text.substring(1)); - return (player, weight) -> weight - arg; - } - case '%' -> { - double arg = Double.parseDouble(text.substring(1)); - return (player, weight) -> weight % arg; - } - case '+' -> { - double arg = Double.parseDouble(text.substring(1)); - return (player, weight) -> weight + arg; - } - case '=' -> { - String formula = text.substring(1); - return (player, weight) -> getExpressionValue(player, formula, Map.of("{0}", String.valueOf(weight))); - } - default -> throw new IllegalArgumentException("Invalid weight: " + text); - } - } - - public static double getExpressionValue(Player player, String formula, Map vars) { - formula = PlaceholderManagerImpl.getInstance().parse(player, formula, vars); - return new ExpressionBuilder(formula).build().evaluate(); - } - - public static ArrayList getReadableSection(Map map) { - ArrayList list = new ArrayList<>(); - mapToReadableStringList(map, list, 0, false); - return list; - } - - @SuppressWarnings("unchecked") - public static void mapToReadableStringList(Map map, List readableList, int loop_times, boolean isMapList) { - boolean first = true; - for (Map.Entry entry : map.entrySet()) { - Object nbt = entry.getValue(); - if (nbt instanceof String value) { - if (isMapList && first) { - first = false; - readableList.add(" ".repeat(loop_times - 1) + "- " + entry.getKey() + ": " + value); - } else { - readableList.add(" ".repeat(loop_times) + "" + entry.getKey() + ": " + value); - } - } else if (nbt instanceof List list) { - if (isMapList && first) { - first = false; - readableList.add(" ".repeat(loop_times - 1) + "- " + entry.getKey() + ":"); - } else { - readableList.add(" ".repeat(loop_times) + "" + entry.getKey() + ":"); - } - for (Object value : list) { - if (value instanceof Map nbtMap) { - mapToReadableStringList((Map) nbtMap, readableList, loop_times + 2, true); - } else { - readableList.add(" ".repeat(loop_times + 1) + "- " + value); - } - } - } else if (nbt instanceof ConfigurationSection section) { - if (isMapList && first) { - first = false; - readableList.add(" ".repeat(loop_times - 1) + "- " + entry.getKey() + ":"); - } else { - readableList.add(" ".repeat(loop_times) + "" + entry.getKey() + ":"); - } - mapToReadableStringList(section.getValues(false), readableList, loop_times + 1, false); - } else if (nbt instanceof Map innerMap) { - if (isMapList && first) { - first = false; - readableList.add(" ".repeat(loop_times - 1) + "- " + entry.getKey() + ":"); - } else { - readableList.add(" ".repeat(loop_times) + "" + entry.getKey() + ":"); - } - mapToReadableStringList((Map) innerMap, readableList, loop_times + 1, false); - } - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/FakeItemUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/FakeItemUtils.java deleted file mode 100644 index d0ae3c8e..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/util/FakeItemUtils.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.util; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.wrappers.WrappedDataValue; -import com.comphenix.protocol.wrappers.WrappedDataWatcher; -import com.google.common.collect.Lists; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import org.bukkit.Location; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.ItemStack; -import org.bukkit.util.Vector; - -import java.util.List; -import java.util.Objects; -import java.util.UUID; - -/** - * Utility class for managing fake item entities using PacketContainers. - */ -public class FakeItemUtils { - - private 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); - entityPacket.getModifier().write(1, UUID.randomUUID()); - entityPacket.getEntityTypeModifier().write(0, EntityType.DROPPED_ITEM); - entityPacket.getDoubles().write(0, location.getX()); - entityPacket.getDoubles().write(1, location.getY() - 0.5); - entityPacket.getDoubles().write(2, location.getZ()); - 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); - if (CustomFishingPlugin.getInstance().getVersionManager().isVersionNewerThan1_19_R2()) { - WrappedDataWatcher wrappedDataWatcher = createDataWatcher(itemStack); - setValueList(metaPacket, wrappedDataWatcher); - } else { - metaPacket.getWatchableCollectionModifier().write(0, createDataWatcher(itemStack).getWatchableObjects()); - } - 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); - tpPacket.getDoubles().write(0, location.getX()); - tpPacket.getDoubles().write(1, location.getY() - 0.5); - tpPacket.getDoubles().write(2, location.getZ()); - return tpPacket; - } - - /** - * 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)); - entityPacket.getIntegers().write(2, (int) (vector.getY() * 8000)); - entityPacket.getIntegers().write(3, (int) (vector.getZ() * 8000)); - 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); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(5, WrappedDataWatcher.Registry.get(Boolean.class)), true); - 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 -> { - final WrappedDataWatcher.WrappedDataWatcherObject dataWatcherObject = entry.getWatcherObject(); - wrappedDataValueList.add(new WrappedDataValue(dataWatcherObject.getIndex(), dataWatcherObject.getSerializer(), entry.getRawValue())); - }); - metaPacket.getDataValueCollectionModifier().write(0, wrappedDataValueList); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/ItemUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/ItemUtils.java deleted file mode 100644 index a2d7d6c9..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ItemUtils.java +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.util; - -import de.tr7zw.changeme.nbtapi.NBTCompound; -import de.tr7zw.changeme.nbtapi.NBTItem; -import de.tr7zw.changeme.nbtapi.NBTList; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.ScoreComponent; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.momirealms.customfishing.adventure.AdventureHelper; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.api.common.Pair; -import net.momirealms.customfishing.api.mechanic.hook.HookSetting; -import net.momirealms.customfishing.api.util.LogUtils; -import net.momirealms.customfishing.setting.CFConfig; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.Player; -import org.bukkit.event.player.PlayerItemDamageEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; -import org.bukkit.inventory.meta.Damageable; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.util.io.BukkitObjectInputStream; -import org.bukkit.util.io.BukkitObjectOutputStream; -import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/** - * Utility class for various item-related operations. - */ -public class ItemUtils { - - private 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) - return nbtItem; - - NBTCompound displayCompound = nbtItem.getOrCreateCompound("display"); - NBTList lore = displayCompound.getStringList("Lore"); - lore.removeIf(it -> GsonComponentSerializer.gson().deserialize(it) instanceof ScoreComponent scoreComponent && scoreComponent.name().equals("cf")); - - if (cfCompound.hasTag("hook_id")) { - String hookID = cfCompound.getString("hook_id"); - HookSetting setting = CustomFishingPlugin.get().getHookManager().getHookSetting(hookID); - if (setting == null) { - cfCompound.removeKey("hook_id"); - cfCompound.removeKey("hook_item"); - cfCompound.removeKey("hook_dur"); - } else { - for (String newLore : setting.getLore()) { - ScoreComponent.Builder builder = Component.score().name("cf").objective("hook"); - builder.append(AdventureHelper.getInstance().getComponentFromMiniMessage( - newLore.replace("{dur}", String.valueOf(cfCompound.getInteger("hook_dur"))) - .replace("{max}", String.valueOf(setting.getMaxDurability())) - )); - lore.add(GsonComponentSerializer.gson().serialize(builder.build())); - } - } - } - - if (cfCompound.hasTag("max_dur")) { - int max = cfCompound.getInteger("max_dur"); - int current = cfCompound.getInteger("cur_dur"); - for (String newLore : CFConfig.durabilityLore) { - ScoreComponent.Builder builder = Component.score().name("cf").objective("durability"); - builder.append(AdventureHelper.getInstance().getComponentFromMiniMessage( - newLore.replace("{dur}", String.valueOf(current)) - .replace("{max}", String.valueOf(max)) - )); - lore.add(GsonComponentSerializer.gson().serialize(builder.build())); - } - } - 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; - NBTItem nbtItem = updateNBTItemLore(new NBTItem(itemStack)); - itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); - } - - /** - * 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(rod); - NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); - if (cfCompound != null && cfCompound.hasTag("hook_dur")) { - int hookDur = cfCompound.getInteger("hook_dur"); - 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); - rod.setItemMeta(nbtItem.getItem().getItemMeta()); - } - - /** - * 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(Player player, ItemStack itemStack, int amount, boolean updateLore) { - if (itemStack == null || itemStack.getType() == Material.AIR) - return; - NBTItem nbtItem = new NBTItem(itemStack); - NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); - if (cfCompound != null && cfCompound.hasTag("max_dur")) { - int unBreakingLevel = itemStack.getEnchantmentLevel(Enchantment.DURABILITY); - if (Math.random() > (double) 1 / (unBreakingLevel + 1)) { - return; - } - if (nbtItem.getByte("Unbreakable") == 1) { - return; - } - int max = cfCompound.getInteger("max_dur"); - int current = cfCompound.getInteger("cur_dur") - amount; - cfCompound.setInteger("cur_dur", current); - int damage = (int) (itemStack.getType().getMaxDurability() * (1 - ((double) current / max))); - nbtItem.setInteger("Damage", damage); - if (current > 0) { - if (updateLore) updateNBTItemLore(nbtItem); - itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); - } else { - itemStack.setAmount(0); - } - } else { - ItemMeta previousMeta = itemStack.getItemMeta().clone(); - PlayerItemDamageEvent itemDamageEvent = new PlayerItemDamageEvent(player, itemStack, amount); - Bukkit.getPluginManager().callEvent(itemDamageEvent); - if (!itemStack.getItemMeta().equals(previousMeta) || itemDamageEvent.isCancelled()) { - return; - } - int unBreakingLevel = itemStack.getEnchantmentLevel(Enchantment.DURABILITY); - if (Math.random() > (double) 1 / (unBreakingLevel + 1)) { - return; - } - if (nbtItem.getByte("Unbreakable") == 1) { - return; - } - int damage = nbtItem.getInteger("Damage") + amount; - if (damage > itemStack.getType().getMaxDurability()) { - itemStack.setAmount(0); - } else { - nbtItem.setInteger("Damage", damage); - if (updateLore) updateNBTItemLore(nbtItem); - itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); - } - } - } - - /** - * 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); - if (nbtItem.getByte("Unbreakable") == 1) { - return; - } - NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); - if (cfCompound != null && cfCompound.hasTag("max_dur")) { - int max = cfCompound.getInteger("max_dur"); - int current = Math.min(max, cfCompound.getInteger("cur_dur") + amount); - cfCompound.setInteger("cur_dur", current); - int damage = (int) (itemStack.getType().getMaxDurability() * (1 - ((double) current / max))); - nbtItem.setInteger("Damage", damage); - if (updateLore) updateNBTItemLore(nbtItem); - } else { - int damage = Math.max(nbtItem.getInteger("Damage") - amount, 0); - nbtItem.setInteger("Damage", damage); - } - 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; - if (amount <= 0) { - itemStack.setAmount(0); - return; - } - NBTItem nbtItem = new NBTItem(itemStack); - if (nbtItem.getByte("Unbreakable") == 1) { - return; - } - NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); - if (cfCompound != null && cfCompound.hasTag("max_dur")) { - int max = cfCompound.getInteger("max_dur"); - amount = Math.min(amount, max); - cfCompound.setInteger("cur_dur", amount); - int damage = (int) (itemStack.getType().getMaxDurability() * (1 - ((double) amount / max))); - nbtItem.setInteger("Damage", damage); - if (updateLore) updateNBTItemLore(nbtItem); - } else { - nbtItem.setInteger("Damage", itemStack.getType().getMaxDurability() - amount); - } - 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 Pair getCustomDurability(ItemStack itemStack) { - if (itemStack == null || itemStack.getType() == Material.AIR) - return Pair.of(0, 0); - if (itemStack.getItemMeta() instanceof Damageable damageable && damageable.isUnbreakable()) - return Pair.of(-1, -1); - NBTItem nbtItem = new NBTItem(itemStack); - NBTCompound cfCompound = nbtItem.getCompound("CustomFishing"); - if (cfCompound != null && cfCompound.hasTag("max_dur")) { - return Pair.of(cfCompound.getInteger("max_dur"), cfCompound.getInteger("cur_dur")); - } else { - return Pair.of(0, 0); - } - } - - /** - * 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 giveItem(Player player, ItemStack itemStack, int amount) { - PlayerInventory inventory = player.getInventory(); - ItemMeta meta = itemStack.getItemMeta(); - int maxStackSize = itemStack.getMaxStackSize(); - - if (amount > maxStackSize * 100) { - LogUtils.warn("Detected too many items spawning. Lowering the amount to " + (maxStackSize * 100)); - amount = maxStackSize * 100; - } - - int actualAmount = amount; - - for (ItemStack other : inventory.getStorageContents()) { - if (other != null) { - if (other.getType() == itemStack.getType() && other.getItemMeta().equals(meta)) { - if (other.getAmount() < maxStackSize) { - int delta = maxStackSize - other.getAmount(); - if (amount > delta) { - other.setAmount(maxStackSize); - amount -= delta; - } else { - other.setAmount(amount + other.getAmount()); - return actualAmount; - } - } - } - } - } - - if (amount > 0) { - for (ItemStack other : inventory.getStorageContents()) { - if (other == null) { - if (amount > maxStackSize) { - amount -= maxStackSize; - ItemStack cloned = itemStack.clone(); - cloned.setAmount(maxStackSize); - inventory.addItem(cloned); - } else { - ItemStack cloned = itemStack.clone(); - cloned.setAmount(amount); - inventory.addItem(cloned); - return actualAmount; - } - } - } - } - - if (amount > 0) { - for (int i = 0; i < amount / maxStackSize; i++) { - ItemStack cloned = itemStack.clone(); - cloned.setAmount(maxStackSize); - player.getWorld().dropItem(player.getLocation(), cloned); - } - int left = amount % maxStackSize; - if (left != 0) { - ItemStack cloned = itemStack.clone(); - cloned.setAmount(left); - player.getWorld().dropItem(player.getLocation(), cloned); - } - } - - return actualAmount; - } - - public static String toBase64(ItemStack itemStack) { - if (itemStack == null || itemStack.getType() == Material.AIR) - return ""; - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try (BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream)) { - dataOutput.writeObject(itemStack); - byte[] byteArr = outputStream.toByteArray(); - dataOutput.close(); - outputStream.close(); - return Base64Coder.encodeLines(byteArr); - } catch (IOException e) { - e.printStackTrace(); - return ""; - } - } - - public static ItemStack fromBase64(String base64) { - if (base64 == null || base64.equals("")) - return new ItemStack(Material.AIR); - ByteArrayInputStream inputStream; - try { - inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(base64)); - } catch (IllegalArgumentException e) { - return new ItemStack(Material.AIR); - } - ItemStack stack = null; - try (BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream)) { - stack = (ItemStack) dataInput.readObject(); - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - } - return stack; - } - - public static ItemStack removeOwner(ItemStack itemStack) { - if (itemStack == null || itemStack.getType() == Material.AIR) return itemStack; - NBTItem nbtItem = new NBTItem(itemStack); - if (nbtItem.hasTag("owner")) { - nbtItem.removeKey("owner"); - return nbtItem.getItem(); - } - return itemStack; - } - - /** - * @return the amount of items that can't be put in the inventory - */ - public static int putLootsToBag(Inventory inventory, ItemStack itemStack, int amount) { - itemStack = removeOwner(itemStack.clone()); - ItemMeta meta = itemStack.getItemMeta(); - int maxStackSize = itemStack.getMaxStackSize(); - for (ItemStack other : inventory.getStorageContents()) { - if (other != null) { - if (other.getType() == itemStack.getType() && other.getItemMeta().equals(meta)) { - if (other.getAmount() < maxStackSize) { - int delta = maxStackSize - other.getAmount(); - if (amount > delta) { - other.setAmount(maxStackSize); - amount -= delta; - } else { - other.setAmount(amount + other.getAmount()); - return 0; - } - } - } - } - } - - if (amount > 0) { - for (ItemStack other : inventory.getStorageContents()) { - if (other == null) { - if (amount > maxStackSize) { - amount -= maxStackSize; - ItemStack cloned = itemStack.clone(); - cloned.setAmount(maxStackSize); - inventory.addItem(cloned); - } else { - ItemStack cloned = itemStack.clone(); - cloned.setAmount(amount); - inventory.addItem(cloned); - return 0; - } - } - } - } - - return amount; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/NBTUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/NBTUtils.java deleted file mode 100644 index 075dea65..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/util/NBTUtils.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customfishing.util; - -import de.tr7zw.changeme.nbtapi.NBTCompound; -import de.tr7zw.changeme.nbtapi.NBTListCompound; -import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT; -import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion; -import de.tr7zw.changeme.nbtapi.utils.VersionChecker; -import net.momirealms.customfishing.api.CustomFishingPlugin; -import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; -import org.bukkit.Bukkit; -import org.bukkit.configuration.MemorySection; -import org.bukkit.entity.Player; - -import java.lang.reflect.Field; -import java.util.*; - -/** - * Utility class for working with NBT (Named Binary Tag) data. - */ -public class NBTUtils { - - private NBTUtils() {} - - public static void disableNBTAPILogs() { - MinecraftVersion.disableBStats(); - MinecraftVersion.disableUpdateCheck(); - VersionChecker.hideOk = true; - try { - Field field = MinecraftVersion.class.getDeclaredField("version"); - field.setAccessible(true); - MinecraftVersion minecraftVersion; - try { - String serverVersion = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; - minecraftVersion = MinecraftVersion.valueOf(serverVersion.replace("v", "MC")); - } catch (Exception ex) { - minecraftVersion = VERSION_TO_REVISION.getOrDefault(Bukkit.getServer().getBukkitVersion().split("-")[0], - MinecraftVersion.UNKNOWN); - } - field.set(MinecraftVersion.class, minecraftVersion); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); - } - boolean hasGsonSupport; - try { - Class.forName("com.google.gson.Gson"); - hasGsonSupport = true; - } catch (Exception ex) { - hasGsonSupport = false; - } - try { - Field field= MinecraftVersion.class.getDeclaredField("hasGsonSupport"); - field.setAccessible(true); - field.set(Boolean.class, hasGsonSupport); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - private static final Map VERSION_TO_REVISION = new HashMap<>() { - { - this.put("1.20", MinecraftVersion.MC1_20_R1); - this.put("1.20.1", MinecraftVersion.MC1_20_R1); - this.put("1.20.2", MinecraftVersion.MC1_20_R2); - this.put("1.20.3", MinecraftVersion.MC1_20_R3); - this.put("1.20.4", MinecraftVersion.MC1_20_R3); - this.put("1.20.5", MinecraftVersion.MC1_20_R4); - this.put("1.20.6", MinecraftVersion.MC1_20_R4); - } - }; - - - /** - * Inner class representing a stack element used during NBT data conversion. - */ - public static class StackElement { - final Map currentMap; - final NBTCompound currentNbtCompound; - - StackElement(Map map, NBTCompound nbtCompound) { - this.currentMap = map; - this.currentNbtCompound = nbtCompound; - } - } - - /** - * 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(Player player, Map placeholders, NBTCompound nbtCompound, Map map) { - - Deque stack = new ArrayDeque<>(); - stack.push(new StackElement(map, nbtCompound)); - - while (!stack.isEmpty()) { - StackElement currentElement = stack.pop(); - Map currentMap = currentElement.currentMap; - NBTCompound currentNbtCompound = currentElement.currentNbtCompound; - - for (Map.Entry entry : currentMap.entrySet()) { - String key = entry.getKey(); - Object value = entry.getValue(); - - if (value instanceof MemorySection memorySection) { - stack.push(new StackElement(memorySection.getValues(false), currentNbtCompound.addCompound(key))); - } else if (value instanceof List list) { - for (Object o : list) { - if (o instanceof String stringValue) { - setListValue(player, placeholders, key, stringValue, currentNbtCompound); - } else if (o instanceof Map mapValue) { - NBTListCompound nbtListCompound = currentNbtCompound.getCompoundList(key).addCompound(); - stack.push(new StackElement((Map) mapValue, nbtListCompound)); - } - } - } else if (value instanceof String stringValue) { - setSingleValue(player, placeholders, key, stringValue, currentNbtCompound); - } - } - } - } - - // Private helper method - private static void setListValue(Player player, Map placeholders, String key, String value, NBTCompound nbtCompound) { - String[] parts = getTypeAndData(value); - String type = parts[0]; - String data = getParsedData(player, placeholders, parts[1]); - switch (type) { - case "String" -> nbtCompound.getStringList(key).add(data); - case "UUID" -> nbtCompound.getUUIDList(key).add(UUID.fromString(data)); - case "Double" -> nbtCompound.getDoubleList(key).add(Double.parseDouble(data)); - case "Long" -> nbtCompound.getLongList(key).add(Long.parseLong(data)); - case "Float" -> nbtCompound.getFloatList(key).add(Float.parseFloat(data)); - case "Int" -> nbtCompound.getIntegerList(key).add(Integer.parseInt(data)); - case "IntArray" -> { - String[] split = data.replace("[", "").replace("]", "").replaceAll("\\s", "").split(","); - int[] array = Arrays.stream(split).mapToInt(Integer::parseInt).toArray(); - nbtCompound.getIntArrayList(key).add(array); - } - default -> throw new IllegalArgumentException("Invalid value type: " + type); - } - } - - // Private helper method - private static void setSingleValue(Player player, Map placeholders, String key, String value, NBTCompound nbtCompound) { - String[] parts = getTypeAndData(value); - String type = parts[0]; - String data = getParsedData(player, placeholders, parts[1]); - switch (type) { - case "Int" -> nbtCompound.setInteger(key, Integer.parseInt(data)); - case "String" -> nbtCompound.setString(key, data); - case "Long" -> nbtCompound.setLong(key, Long.parseLong(data)); - case "Float" -> nbtCompound.setFloat(key, Float.parseFloat(data)); - case "Double" -> nbtCompound.setDouble(key, Double.parseDouble(data)); - case "Short" -> nbtCompound.setShort(key, Short.parseShort(data)); - case "Boolean" -> nbtCompound.setBoolean(key, Boolean.parseBoolean(data)); - case "UUID" -> nbtCompound.setUUID(key, UUID.nameUUIDFromBytes(data.getBytes())); - case "Byte" -> nbtCompound.setByte(key, Byte.parseByte(data)); - case "ByteArray" -> { - String[] split = splitValue(value); - byte[] bytes = new byte[split.length]; - for (int i = 0; i < split.length; i++){ - bytes[i] = Byte.parseByte(split[i]); - } - nbtCompound.setByteArray(key, bytes); - } - case "IntArray" -> { - String[] split = splitValue(value); - int[] array = Arrays.stream(split).mapToInt(Integer::parseInt).toArray(); - nbtCompound.setIntArray(key, array); - } - default -> throw new IllegalArgumentException("Invalid value type: " + type); - } - } - - public static String getParsedData(Player player, Map placeholders, String data) { - if (data.length() >= 3) - switch (data.substring(0,3)) { - case "-P:" -> data = PlaceholderManagerImpl.getInstance().parse(player, data.substring(3), placeholders); - case "-E:" -> { - double value = ConfigUtils.getExpressionValue(player, data.substring(3), placeholders); - if (value % 1 == 0) { - data = Long.toString((long) value); - } else { - data = Double.toString(value); - } - } - } - return data; - } - - /** - * 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()) { - switch (nbtCompound.getType(key)){ - case NBTTagByte -> map.put(key, "(Byte) " + nbtCompound.getByte(key)); - case NBTTagInt -> map.put(key, "(Int) " + nbtCompound.getInteger(key)); - case NBTTagDouble -> map.put(key, "(Double) " + nbtCompound.getDouble(key)); - case NBTTagLong -> map.put(key, "(Long) " + nbtCompound.getLong(key)); - case NBTTagFloat -> map.put(key, "(Float) " + nbtCompound.getFloat(key)); - case NBTTagShort -> map.put(key, "(Short) " + nbtCompound.getShort(key)); - case NBTTagString -> map.put(key, "(String) " + nbtCompound.getString(key)); - case NBTTagByteArray -> map.put(key, "(ByteArray) " + Arrays.toString(nbtCompound.getByteArray(key))); - case NBTTagIntArray -> map.put(key, "(IntArray) " + Arrays.toString(nbtCompound.getIntArray(key))); - case NBTTagCompound -> { - Map map1 = compoundToMap(Objects.requireNonNull(nbtCompound.getCompound(key))); - if (!map1.isEmpty()) map.put(key, map1); - } - case NBTTagList -> { - List list = new ArrayList<>(); - switch (Objects.requireNonNull(nbtCompound.getListType(key))) { - case NBTTagCompound -> nbtCompound.getCompoundList(key).forEach(a -> list.add(compoundToMap(a))); - case NBTTagInt -> nbtCompound.getIntegerList(key).forEach(a -> list.add("(Int) " + a)); - case NBTTagDouble -> nbtCompound.getDoubleList(key).forEach(a -> list.add("(Double) " + a)); - case NBTTagString -> nbtCompound.getStringList(key).forEach(a -> list.add("(String) " + a)); - case NBTTagFloat -> nbtCompound.getFloatList(key).forEach(a -> list.add("(Float) " + a)); - case NBTTagLong -> nbtCompound.getLongList(key).forEach(a -> list.add("(Long) " + a)); - case NBTTagIntArray -> nbtCompound.getIntArrayList(key).forEach(a -> list.add("(IntArray) " + Arrays.toString(a))); - } - if (!list.isEmpty()) map.put(key, list); - } - } - } - return map; - } - - /** - * Splits a value into type and data components. - * - * @param str The input value string - * @return An array containing type and data strings - */ - public static String[] getTypeAndData(String str) { - String[] parts = str.split("\\s+", 2); - if (parts.length == 1) { - return new String[]{"String", str}; - } - if (parts.length != 2) { - throw new IllegalArgumentException("Invalid value format: " + str); - } - String type = parts[0].substring(1, parts[0].length() - 1); - String data = parts[1]; - 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 - */ - public static String[] splitValue(String value) { - return value.substring(value.indexOf('[') + 1, value.lastIndexOf(']')) - .replaceAll("\\s", "") - .split(","); - } -} \ No newline at end of file diff --git a/plugin/src/main/resources/market.yml b/plugin/src/main/resources/market.yml deleted file mode 100644 index e53f5380..00000000 --- a/plugin/src/main/resources/market.yml +++ /dev/null @@ -1,158 +0,0 @@ -enable: true - -# Container title -title: 'Fish Market' - -limitation: - enable: true - # Support expression and placeholders - earnings: '10000' - -# Market menu layout -layout: - - 'AAAAAAAAA' - - 'AIIIIIIIA' - - 'AIIIIIIIA' - - 'AIIIIIIIA' - - 'AAAABAAAA' - -# Price formula (For CustomFishing loots) -price-formula: '{BASE} + {BONUS} * {SIZE}' - -# Item price (For vanilla items & other plugin items that have CustomModelData) -item-price: - # Vanilla Items - COD: 10 - PUFFERFISH: 10 - SALMON: 10 - TROPICAL_FISH: 10 - # PAPER (CustomModelData: 999) - PAPER:999: 5 - -# Slots to put items in -item-slot: - symbol: 'I' - allow-items-with-no-price: true - -# This is an icon that allows players to sell all the fish from their inventory and fishingbag -# You can enable it by putting the symbol into layout -sell-all-icons: - symbol: 'S' - # Should the fish in fishing bag be sold - fishingbag: true - allow-icon: - material: IRON_BLOCK - display: - name: '<#00CED1>Ship the fish' - lore: - - 'You will get {money_formatted}$ by selling the fish from inventory and bag' - action: - sound_action: - type: sound - value: - key: 'minecraft:block.amethyst_block.place' - source: 'player' - volume: 1 - pitch: 1 - message_action: - type: message - value: 'You earned {money_formatted}$ by selling the fish! You can still get {rest_formatted}$ from market today' - command_action: - type: command - value: 'money give {player} {money}' -# Requires Vault and any economy plugin -# money_action: -# type: give-money -# value: '{money}' - deny-icon: - material: REDSTONE_BLOCK - display: - name: 'Denied trade' - lore: - - 'Nothing to sell!' - action: - sound_action: - type: sound - value: - key: 'minecraft:entity.villager.no' - source: 'player' - volume: 1 - pitch: 1 - limit-icon: - material: REDSTONE_BLOCK - display: - name: 'Denied trade' - lore: - - 'The worth of items exceeds the money that can be earned for the rest of today!' - action: - sound_action: - type: sound - value: - key: 'minecraft:block.anvil.land' - source: 'player' - volume: 1 - pitch: 1 - -# Sell icon -sell-icons: - symbol: 'B' - allow-icon: - material: IRON_BLOCK - display: - name: '<#00CED1>Ship the fish' - lore: - - 'You will get {money_formatted}$ by selling the fish' - action: - sound_action: - type: sound - value: - key: 'minecraft:block.amethyst_block.place' - source: 'player' - volume: 1 - pitch: 1 - message_action: - type: message - value: 'You earned {money_formatted}$ by selling the fish! You can still get {rest_formatted}$ from market today' - command_action: - type: command - value: 'money give {player} {money}' -# Requires Vault and any economy plugin -# money_action: -# type: give-money -# value: '{money}' - deny-icon: - material: REDSTONE_BLOCK - display: - name: 'Denied trade' - lore: - - 'Nothing to sell!' - action: - sound_action: - type: sound - value: - key: 'minecraft:entity.villager.no' - source: 'player' - volume: 1 - pitch: 1 - limit-icon: - material: REDSTONE_BLOCK - display: - name: 'Denied trade' - lore: - - 'The worth of items exceeds the money that can be earned for the rest of today!' - action: - sound_action: - type: sound - value: - key: 'minecraft:block.anvil.land' - source: 'player' - volume: 1 - pitch: 1 - -# Decorative icons -decorative-icons: - glass-pane: - symbol: 'A' - material: BLACK_STAINED_GLASS_PANE - display: - name: ' ' \ No newline at end of file diff --git a/plugin/src/main/resources/messages/en.yml b/plugin/src/main/resources/messages/en.yml deleted file mode 100644 index 82ef3fa2..00000000 --- a/plugin/src/main/resources/messages/en.yml +++ /dev/null @@ -1,125 +0,0 @@ -# Don't change this -config-version: '31' - -messages: - prefix: '[CustomFishing] ' - reload: 'Reloaded. Took {time}ms.' - item-not-exist: 'Item not found.' - give-item: 'Successfully given player {player} {amount}x {item}.' - get-item: 'Successfully got {amount}x {item}.' - possible-loots: 'Possible loots here: ' - split-char: ', ' - competition-not-exist: 'Competition {id} does not exist.' - no-competition-ongoing: "There's no competition ongoing." - stop-competition: 'Stopped the current competition.' - end-competition: 'Ended the current competition.' - no-score: 'No Score' - no-player: 'No Player' - no-rank: 'No Rank' - goal-catch-amount: 'Fish count caught' - goal-max-size: 'Largest fish caught' - goal-total-size: 'Total length of fish caught' - goal-total-score: 'Cumulative score of fish caught' - unsafe-modification: "Cannot modify a player's fishing bag if they're active on another linked server." - never-played: "The player hasn't joined the server before. Can't modify a nonexistent player's fishing bag." - data-not-loaded: "Data hasn't loaded. Please re-enter the server. If issues persist, reach out to the server admin." - open-market-gui: "Successfully opened the market gui for {player}" - open-fishing-bag: "Successfully opened the fishing bag for {player}" - format-day: 'd' - format-hour: 'h' - format-minute: 'm' - format-second: 's' - -gui: - search: "Search" - select-file: 'Select file' - select-item: "Select item" - dupe-invalid-key: "● Duplicated or invalid key" - new-value: "New value: " - temp-new-key: 'New key' - set-new-key: "Set new key" - edit-key: 'Edit {0}' - delete-property: "<#00CED1>● Delete property" - click-confirm: "<#00FF7F> -> Click to confirm" - invalid-number: "● Invalid number" - illegal-format: "● Illegal format" - scroll-up: '● Scroll up' - scroll-down: '● Scroll down' - cannot-scroll-up: "You've reached the top" - cannot-scroll-down: "You can't scroll further down" - next-page: '● Next Page' - goto-next-page: 'Go to page {0} / {1}' - cannot-goto-next-page: 'There are no more pages' - previous-page: '● Previous page' - goto-previous-page: 'Go to page {0} / {1}' - cannot-goto-previous-page: "You can't go further back" - back-to-parent-page: "<#FF8C00>Back to parent page" - back-to-parent-folder: "<#FF8C00>Back to parent folder" - current-value: "Current value: " - click-to-toggle: "<#00FF7F> -> Click to toggle" - left-click-edit: "<#00FF7F> -> Left click to edit" - right-click-reset: "<#FF6347> -> Right click to reset" - right-click-delete: "<#FF6347> -> Right click to delete" - right-click-cancel: "<#00CED1> -> Right click to cancel" - loot-show-in-finder: "<#5F9EA0>● Show In Fish Finder" - loot-score: "<#FF1493>● Score" - loot-nick: "<#00FF00>● Nick" - loot-instant-game: "<#7B68EE>● Instant Game" - loot-disable-statistics: "<#CD853F>● Disable Statistics" - loot-disable-game: "<#8B4513>● Disable Game" - item-amount: "<#1E90FF>● Amount" - item-custom-model-data: "<#FFC0CB>● Custom Model Data" - item-display-name: "<#FAFAD2>● Display Name" - item-custom-durability: "<#1E90FF>● Custom Durability" - item-enchantment: "<#8A2BE2>● Enchantment" - item-head64: "<#2E8B57>● Head64" - item-item-flag: "<#E6E6FA>● Item Flag" - item-lore: "<#FA8072>● Lore" - item-material: "<#FF00FF>● Material" - item-nbt: "<#FA8072>● NBT" - item-prevent-grab: "<#FF4500>● Prevent Grabbing" - item-price: "<#FFD700>● Price" - item-price-base: " - base: " - item-price-bonus: " - bonus: " - item-random-durability: "<#FFFF00>● Random Durability" - item-size: "<#FFF0F5>● Size" - item-stackable: "<#9370DB>● Stackable" - item-stored-enchantment: "<#9370DB>● Stored Enchantment" - item-tag: "<#2E8B57>● Tag" - item-unbreakable: "<#C0C0C0>● Unbreakable" - page-amount-title: "Edit Amount" - page-model-data-title: "Edit CustomModelData" - page-display-name-title: "Edit display name" - page-new-display-name: "New name" - page-custom-durability-title: "Edit custom durability" - page-stored-enchantment-title: "Edit stored enchantment" - page-enchantment-title: "Edit enchantment" - page-select-one-enchantment: "Select one enchantment" - page-add-new-enchantment: "[+] Add a new enchantment" - page-item-flag-title: "Edit item flag" - page-lore-title: "Edit lore" - page-add-new-lore: "[+] Add a new line" - page-select-one-lore: "Select one line" - page-material-title: "Edit Material" - page-nbt-compound-key-title: "Edit compound key" - page-nbt-list-key-title: "Edit list key" - page-nbt-key-title: "Edit key" - page-nbt-invalid-key: "Invaild key" - page-nbt-add-new-compound: "[+] Add a new compound" - page-nbt-add-new-list: "[+] Add a new list" - page-nbt-add-new-value: "[+] Add a new value" - page-add-new-key: "[+] Add a new key" - page-nbt-preview: "● NBT Preview" - page-nbt-back-to-compound: "Back to parent compound" - page-nbt-set-value-title: "Set value" - page-nbt-edit-title: "Edit NBT" - page-nick-title: "Edit nick" - page-new-nick: "New nick" - page-price-title: "Edit price" - page-base-price: "Base" - page-base-bonus: "Bonus" - page-score-title: "Edit score" - page-size-title: "Edit size" - page-size-min: "Minimum" - page-size-max: "Maximum" - page-size-max-no-less-min: "● Max must be no less than min" diff --git a/plugin/src/main/resources/messages/es.yml b/plugin/src/main/resources/messages/es.yml deleted file mode 100644 index 11405083..00000000 --- a/plugin/src/main/resources/messages/es.yml +++ /dev/null @@ -1,125 +0,0 @@ -# Don't change this -config-version: '31' - -messages: - prefix: '[CustomFishing] ' - reload: 'Recargado! Tardó {time}ms.' - item-not-exist: 'Item no encontrado.' - give-item: 'Se ha entregado {amount}x {item} al jugador {player}.' - get-item: 'Se ha recibido {amount}x {item}.' - possible-loots: 'Posibles recompensas: ' - split-char: ', ' - competition-not-exist: 'La Competición {id} no existe.' - no-competition-ongoing: "No hay una competencia activa." - stop-competition: 'Se ha detenido la competición.' - end-competition: 'Ha finalizado la competición.' - no-score: 'Sin puntaje' - no-player: 'Sin jugador' - no-rank: 'Sin rango' - goal-catch-amount: 'Peces atrapados' - goal-max-size: 'Pez más grande capturado' - goal-total-size: 'Longitud total de peces capturados' - goal-total-score: 'Puntaje acumulado por peces capturados' - unsafe-modification: "No se puede modificar la bolsa de pesca de un jugador si sigue activo en otro servidor vinculado." - never-played: "Este jugador nunca se ha unido al servidor. No puedes modificar la bolsa de pesca de un jugador inexistente." - data-not-loaded: "No se pudo cargar la información. Por favor, vuelve a ingresar al servidor. Si el problema persiste, contacta a un administrador." - open-market-gui: "Se ha abierto la interfaz para {player}" - open-fishing-bag: "Se ha abierto la bolsa de pesca de {player}" - format-day: 'd' - format-hour: 'h' - format-minute: 'm' - format-second: 's' - -gui: - search: "Buscar" - select-file: 'Seleccionar archivo' - select-item: "Seleccionar item" - dupe-invalid-key: "● ID inválida o duplicada" - new-value: "Nuevo valor: " - temp-new-key: 'Nueva ID' - set-new-key: "Definir nueva ID" - edit-key: 'Editar {0}' - delete-property: "<#00CED1>● Eliminar propiedad" - click-confirm: "<#00FF7F> -> Click para confirmar" - invalid-number: "● Número inválido" - illegal-format: "● Formato inválido" - scroll-up: '● Arriba' - scroll-down: '● Abajo' - cannot-scroll-up: "Estás en el límite superior" - cannot-scroll-down: "Estás en el límite inferior" - next-page: '● Siguiente Página' - goto-next-page: 'Ir a la página {0} / {1}' - cannot-goto-next-page: 'No hay más páginas' - previous-page: '● Página anterior' - goto-previous-page: 'Ir a la página {0} / {1}' - cannot-goto-previous-page: "No hay más páginas atrás" - back-to-parent-page: "<#FF8C00>Volver al menú anterior" - back-to-parent-folder: "<#FF8C00>Volver a la carpeta anterior" - current-value: "Valor actual: " - click-to-toggle: "<#00FF7F> -> Click para cambiar" - left-click-edit: "<#00FF7F> -> Click izquierdo para editar" - right-click-reset: "<#FF6347> -> Click derecho para restablecer" - right-click-delete: "<#FF6347> -> Click derecho para eliminar" - right-click-cancel: "<#00CED1> -> Click derecho para cancelar" - loot-show-in-finder: "<#5F9EA0>● Mostar en el rastreador de peces" - loot-score: "<#FF1493>● Puntaje" - loot-nick: "<#00FF00>● Apodo" - loot-instant-game: "<#7B68EE>● Juego Instantáneo" - loot-disable-statistics: "<#CD853F>● Deshabilitar Estadísticas" - loot-disable-game: "<#8B4513>● Deshabilitar Juego" - item-amount: "<#1E90FF>● Cantidad" - item-custom-model-data: "<#FFC0CB>● Custom Model Data" - item-display-name: "<#FAFAD2>● Nombre a mostrar" - item-custom-durability: "<#1E90FF>● Durabilidad" - item-enchantment: "<#8A2BE2>● Encantamiento" - item-head64: "<#2E8B57>● Head64" - item-item-flag: "<#E6E6FA>● Item Flag" - item-lore: "<#FA8072>● Descripción" - item-material: "<#FF00FF>● Material" - item-nbt: "<#FA8072>● NBT" - item-prevent-grab: "<#FF4500>● Evitar recoger item" - item-price: "<#FFD700>● Precio" - item-price-base: " - base: " - item-price-bonus: " - bonus: " - item-random-durability: "<#FFFF00>● Durabilidad Aleatoria" - item-size: "<#FFF0F5>● Tamaño" - item-stackable: "<#9370DB>● Acumulable" - item-stored-enchantment: "<#9370DB>● Encantamiento Almacenado" - item-tag: "<#2E8B57>● Etiqueta" - item-unbreakable: "<#C0C0C0>● Irrompible" - page-amount-title: "Editar Cantidad" - page-model-data-title: "Editar CustomModelData" - page-display-name-title: "Editar nombre a mostrar" - page-new-display-name: "Nuevo nombre" - page-custom-durability-title: "Edit durabilidad" - page-stored-enchantment-title: "Edit encantamiento almacenado" - page-enchantment-title: "Editar enchantmentamiento" - page-select-one-enchantment: "Seleccionar un encantamiento" - page-add-new-enchantment: "[+] Añadir un encantamiento" - page-item-flag-title: "Editar item flag" - page-lore-title: "Editar descripción" - page-add-new-lore: "[+] Añadir nueva línea" - page-select-one-lore: "Seleccionar una línea" - page-material-title: "Editar Material" - page-nbt-compound-key-title: "Editar ID de NBT compuesta" - page-nbt-list-key-title: "Editar lista de IDs" - page-nbt-key-title: "Editar ID" - page-nbt-invalid-key: "ID inválida" - page-nbt-add-new-compound: "[+] Añadir nueva NBT compuesta" - page-nbt-add-new-list: "[+] Añadir nueva lista" - page-nbt-add-new-value: "[+] Añadir nuevo valor" - page-add-new-key: "[+] Añadir nueva ID" - page-nbt-preview: "● Vista previa de NBT" - page-nbt-back-to-compound: "Volver al compuesto padre" - page-nbt-set-value-title: "Definir nuevo valor" - page-nbt-edit-title: "Editar NBT" - page-nick-title: "Editar apodo" - page-new-nick: "Nuevo apodo" - page-price-title: "Editar precio" - page-base-price: "Base" - page-base-bonus: "Bonus" - page-score-title: "Editar puntuación" - page-size-title: "Editar tamaño" - page-size-min: "Mínimo" - page-size-max: "Máximo" - page-size-max-no-less-min: "● El valor máximo no puede ser inferior al mínimo" diff --git a/plugin/src/main/resources/messages/fr.yml b/plugin/src/main/resources/messages/fr.yml deleted file mode 100644 index abf04c67..00000000 --- a/plugin/src/main/resources/messages/fr.yml +++ /dev/null @@ -1,125 +0,0 @@ -# Don't change this -config-version: '30' - -messages: - prefix: '[CustomFishing] ' - reload: 'Rechargé. A pris {time}ms.' - item-not-exist: 'Objet non trouvé.' - give-item: 'Joueur {player} a reçu avec succès {amount}x {item}.' - get-item: 'A obtenu avec succès {amount}x {item}.' - possible-loots: 'Butins possibles ici : ' - split-char: ', ' - competition-not-exist: "La compétition {id} n'existe pas." - no-competition-ongoing: 'Aucune compétition en cours.' - stop-competition: 'Compétition actuelle arrêtée.' - end-competition: 'Compétition actuelle terminée.' - no-score: 'Pas de score' - no-player: 'Pas de joueur' - no-rank: 'Pas de classement' - goal-catch-amount: 'Nombre de poissons attrapés' - goal-max-size: 'Plus gros poisson attrapé' - goal-total-size: 'Longueur totale des poissons attrapés' - goal-total-score: 'Score cumulatif des poissons attrapés' - unsafe-modification: "Impossible de modifier le sac de pêche d'un joueur s'il est actif sur un autre serveur lié." - never-played: "Le joueur n'a jamais rejoint le serveur auparavant. Impossible de modifier le sac de pêche d'un joueur inexistant." - data-not-loaded: "Les données n'ont pas été chargées. Veuillez réessayer de rejoindre le serveur. Si les problèmes persistent, contactez l'administrateur du serveur." - open-market-gui: "L'interface du marché a été ouverte avec succès pour {player}." - open-fishing-bag: "Le sac de pêche de {player} a été ouvert avec succès." - format-day: 'j' - format-hour: 'h' - format-minute: 'm' - format-second: 's' - -gui: - search: "Rechercher" - select-file: "Sélectionner un fichier" - select-item: "Sélectionner un élément" - dupe-invalid-key: "● Clé en double ou invalide" - new-value: "Nouvelle valeur : " - temp-new-key: "Nouvelle clé" - set-new-key: "Définir une nouvelle clé" - edit-key: "Modifier {0}" - delete-property: "<#00CED1>● Supprimer la propriété" - click-confirm: "<#00FF7F> -> Cliquez pour confirmer" - invalid-number: "● Nombre invalide" - illegal-format: "● Format illégal" - scroll-up: "● Faire défiler vers le haut" - scroll-down: "● Faire défiler vers le bas" - cannot-scroll-up: "Vous avez atteint le haut" - cannot-scroll-down: "Vous ne pouvez pas faire défiler plus bas" - next-page: "● Page suivante" - goto-next-page: "Aller à la page {0} / {1}" - cannot-goto-next-page: "Il n'y a plus de pages" - previous-page: "● Page précédente" - goto-previous-page: "Aller à la page {0} / {1}" - cannot-goto-previous-page: "Vous ne pouvez pas revenir en arrière" - back-to-parent-page: "<#FF8C00>Retour à la page parente" - back-to-parent-folder: "<#FF8C00>Retour au dossier parent" - current-value: "Valeur actuelle : " - click-to-toggle: "<#00FF7F> -> Cliquez pour basculer" - left-click-edit: "<#00FF7F> -> Cliquez gauche pour modifier" - right-click-reset: "<#FF6347> -> Cliquez droit pour réinitialiser" - right-click-delete: "<#FF6347> -> Cliquez droit pour supprimer" - right-click-cancel: "<#00CED1> -> Cliquez droit pour annuler" - loot-show-in-finder: "<#5F9EA0>● Afficher dans le repère de poisson" - loot-score: "<#FF1493>● Score" - loot-nick: "<#00FF00>● Surnom" - loot-instant-game: "<#7B68EE>● Jeu instantané" - loot-disable-statistics: "<#CD853F>● Désactiver les statistiques" - loot-disable-game: "<#8B4513>● Désactiver le jeu" - item-amount: "<#1E90FF>● Quantité" - item-custom-model-data: "<#FFC0CB>● Données de modèle personnalisées" - item-display-name: "<#FAFAD2>● Nom affiché" - item-custom-durability: "<#1E90FF>● Durabilité personnalisée" - item-enchantment: "<#8A2BE2>● Enchantement" - item-head64: "<#2E8B57>● Tête64" - item-item-flag: "<#E6E6FA>● Drapeau d'élément" - item-lore: "<#FA8072>● Légende" - item-material: "<#FF00FF>● Matériau" - item-nbt: "<#FA8072>● NBT" - item-prevent-grab: "<#FF4500>● Empêcher la saisie" - item-price: "<#FFD700>● Prix" - item-price-base: " - de base : " - item-price-bonus: " - bonus : " - item-random-durability: "<#FFFF00>● Durabilité aléatoire" - item-size: "<#FFF0F5>● Taille" - item-stackable: "<#9370DB>● Empilable" - item-stored-enchantment: "<#9370DB>● Enchantement stocké" - item-tag: "<#2E8B57>● Étiquette" - item-unbreakable: "<#C0C0C0>● Incassable" - page-amount-title: "Modifier la quantité" - page-model-data-title: "Modifier CustomModelData" - page-display-name-title: "Modifier le nom affiché" - page-new-display-name: "Nouveau nom" - page-custom-durability-title: "Modifier la durabilité personnalisée" - page-stored-enchantment-title: "Modifier l'enchantement stocké" - page-enchantment-title: "Modifier l'enchantement" - page-select-one-enchantment: "Sélectionner un enchantement" - page-add-new-enchantment: "[+] Ajouter un nouvel enchantement" - page-item-flag-title: "Modifier le drapeau d'élément" - page-lore-title: "Modifier la légende" - page-add-new-lore: "[+] Ajouter une nouvelle ligne" - page-select-one-lore: "Sélectionner une ligne" - page-material-title: "Modifier le matériau" - page-nbt-compound-key-title: "Modifier la clé composée" - page-nbt-list-key-title: "Modifier la clé de liste" - page-nbt-key-title: "Modifier la clé" - page-nbt-invalid-key: "Clé invalide" - page-nbt-add-new-compound: "[+] Ajouter une nouvelle composante" - page-nbt-add-new-list: "[+] Ajouter une nouvelle liste" - page-nbt-add-new-value: "[+] Ajouter une nouvelle valeur" - page-add-new-key: "[+] Ajouter une nouvelle clé" - page-nbt-preview: "● Aperçu NBT" - page-nbt-back-to-compound: "Retour à la composante parente" - page-nbt-set-value-title: "Définir la valeur" - page-nbt-edit-title: "Modifier NBT" - page-nick-title: "Modifier le surnom" - page-new-nick: "Nouveau surnom" - page-price-title: "Modifier le prix" - page-base-price: "De base" - page-base-bonus: "Bonus" - page-score-title: "Modifier le score" - page-size-title: "Modifier la taille" - page-size-min: "Minimum" - page-size-max: "Maximum" - page-size-max-no-less-min: "Le maximum ne doit pas être inférieur au minimum" diff --git a/plugin/src/main/resources/messages/hu.yml b/plugin/src/main/resources/messages/hu.yml deleted file mode 100644 index 49ac3b02..00000000 --- a/plugin/src/main/resources/messages/hu.yml +++ /dev/null @@ -1,126 +0,0 @@ -# Don't change this -config-version: '30' - -messages: - prefix: '[CustomFishing] ' - reload: 'Újratöltve. Időtartam: {time}ms.' - item-not-exist: 'Tétel nem található.' - give-item: 'Sikeresen adva {player} játékosnak {amount}x {item}.' - get-item: 'Sikeresen megszerezve {amount}x {item}.' - possible-loots: 'Lehetséges kapások itt: ' - split-char: ', ' - competition-not-exist: 'A(z) {id} verseny nem létezik.' - no-competition-ongoing: "Nincs folyamatban lévő verseny." - stop-competition: 'Leállítottad a jelenlegi versenyt.' - end-competition: 'Befejezted a jelenlegi versenyt.' - no-score: 'Nincs pontszám' - no-player: 'Nincs játékos' - no-rank: 'Nincs rang' - goal-catch-amount: 'Kifogott halak száma' - goal-max-size: 'Legnagyobb kifogott hal' - goal-total-size: 'Összes kifogott hal hossza' - goal-total-score: 'Kifogott halak összpontszáma' - unsafe-modification: "Nem lehet módosítani egy játékos horgász táskáját, ha aktív egy másik szerveren." - never-played: "A játékos még nem csatlakozott a szerverhez. Nem lehet módosítani egy nem létező játékos horgász táskáját." - data-not-loaded: "Az adatok nem lettek betöltve. Kérjük, csatlakozz újra a szerverhez. Ha a probléma továbbra is fennáll, fordulj a szerveradminisztrátorhoz." - open-market-gui: "A piac menü sikeresen meg lett nyitva {player} részére" - open-fishing-bag: "A horgász táska sikeresen meg lett nyitva {player} részére" - format-day: 'n' - format-hour: 'ó' - format-minute: 'p' - format-second: 'mp' - -# We're looking for a translator :D -gui: - search: "Search" - select-file: 'Select file' - select-item: "Select item" - dupe-invalid-key: "● Duplicated or invalid key" - new-value: "New value: " - temp-new-key: 'New key' - set-new-key: "Set new key" - edit-key: 'Edit {0}' - delete-property: "<#00CED1>● Delete property" - click-confirm: "<#00FF7F> -> Click to confirm" - invalid-number: "● Invalid number" - illegal-format: "● Illegal format" - scroll-up: '● Scroll up' - scroll-down: '● Scroll down' - cannot-scroll-up: "You've reached the top" - cannot-scroll-down: "You can't scroll further down" - next-page: '● Next Page' - goto-next-page: 'Go to page {0} / {1}' - cannot-goto-next-page: 'There are no more pages' - previous-page: '● Previous page' - goto-previous-page: 'Go to page {0} / {1}' - cannot-goto-previous-page: "You can't go further back" - back-to-parent-page: "<#FF8C00>Back to parent page" - back-to-parent-folder: "<#FF8C00>Back to parent folder" - current-value: "Current value: " - click-to-toggle: "<#00FF7F> -> Click to toggle" - left-click-edit: "<#00FF7F> -> Left click to edit" - right-click-reset: "<#FF6347> -> Right click to reset" - right-click-delete: "<#FF6347> -> Right click to delete" - right-click-cancel: "<#00CED1> -> Right click to cancel" - loot-show-in-finder: "<#5F9EA0>● Show In Fish Finder" - loot-score: "<#FF1493>● Score" - loot-nick: "<#00FF00>● Nick" - loot-instant-game: "<#7B68EE>● Instant Game" - loot-disable-statistics: "<#CD853F>● Disable Statistics" - loot-disable-game: "<#8B4513>● Disable Game" - item-amount: "<#1E90FF>● Amount" - item-custom-model-data: "<#FFC0CB>● Custom Model Data" - item-display-name: "<#FAFAD2>● Display Name" - item-custom-durability: "<#1E90FF>● Custom Durability" - item-enchantment: "<#8A2BE2>● Enchantment" - item-head64: "<#2E8B57>● Head64" - item-item-flag: "<#E6E6FA>● Item Flag" - item-lore: "<#FA8072>● Lore" - item-material: "<#FF00FF>● Material" - item-nbt: "<#FA8072>● NBT" - item-prevent-grab: "<#FF4500>● Prevent Grabbing" - item-price: "<#FFD700>● Price" - item-price-base: " - base: " - item-price-bonus: " - bonus: " - item-random-durability: "<#FFFF00>● Random Durability" - item-size: "<#FFF0F5>● Size" - item-stackable: "<#9370DB>● Stackable" - item-stored-enchantment: "<#9370DB>● Stored Enchantment" - item-tag: "<#2E8B57>● Tag" - item-unbreakable: "<#C0C0C0>● Unbreakable" - page-amount-title: "Edit Amount" - page-model-data-title: "Edit CustomModelData" - page-display-name-title: "Edit display name" - page-new-display-name: "New name" - page-custom-durability-title: "Edit custom durability" - page-stored-enchantment-title: "Edit stored enchantment" - page-enchantment-title: "Edit enchantment" - page-select-one-enchantment: "Select one enchantment" - page-add-new-enchantment: "[+] Add a new enchantment" - page-item-flag-title: "Edit item flag" - page-lore-title: "Edit lore" - page-add-new-lore: "[+] Add a new line" - page-select-one-lore: "Select one line" - page-material-title: "Edit Material" - page-nbt-compound-key-title: "Edit compound key" - page-nbt-list-key-title: "Edit list key" - page-nbt-key-title: "Edit key" - page-nbt-invalid-key: "Invaild key" - page-nbt-add-new-compound: "[+] Add a new compound" - page-nbt-add-new-list: "[+] Add a new list" - page-nbt-add-new-value: "[+] Add a new value" - page-add-new-key: "[+] Add a new key" - page-nbt-preview: "● NBT Preview" - page-nbt-back-to-compound: "Back to parent compound" - page-nbt-set-value-title: "Set value" - page-nbt-edit-title: "Edit NBT" - page-nick-title: "Edit nick" - page-new-nick: "New nick" - page-price-title: "Edit price" - page-base-price: "Base" - page-base-bonus: "Bonus" - page-score-title: "Edit score" - page-size-title: "Edit size" - page-size-min: "Minimum" - page-size-max: "Maximum" - page-size-max-no-less-min: "● Max must be no less than min" diff --git a/plugin/src/main/resources/messages/kr.yml b/plugin/src/main/resources/messages/kr.yml deleted file mode 100644 index da3cc49b..00000000 --- a/plugin/src/main/resources/messages/kr.yml +++ /dev/null @@ -1,125 +0,0 @@ -# Don't change this -config-version: '31' - -messages: - prefix: '[CustomFishing] ' - reload: '리로드 했습니다. {time}ms 소요.' - item-not-exist: '아이템을 찾을 수 없습니다.' - give-item: '플레이어 {player}에게 {item} {amount}개를 지급했습니다.' - get-item: '{item} {amount}개를 얻었습니다.' - possible-loots: '획득 가능한 전리품: ' - split-char: ', ' - competition-not-exist: '대회 {id}은(는) 존재하지 않습니다.' - no-competition-ongoing: "진행 중인 대회가 없습니다." - stop-competition: '현재 진행중인 대회를 중단했습니다.' - end-competition: '현재 진행중인 대회를 종료했습니다.' - no-score: '점수 없음' - no-player: '플레이어 없음' - no-rank: '랭크 없음' - goal-catch-amount: '잡은 물고기의 수' - goal-max-size: '잡은 가장 큰 물고기의 크기' - goal-total-size: '잡은 물고기의 총 길이' - goal-total-score: '잡은 물고기의 누적 점수' - unsafe-modification: "연결된 다른 서버에서 플레이어의 낚시 가방이 활성화된 경우 플레이어의 낚시 가방을 수정할 수 없습니다." - never-played: "해당 플레이어는 이전에 서버에 참여한 적이 없습니다. 존재하지 않는 플레이어의 낚시 가방을 수정할 수 없습니다." - data-not-loaded: "데이터가 불러와지지 않았습니다. 서버에 다시 참여해주세요. 해당 문제가 계속 발생한다면. 관리자에게 문의하세요." - open-market-gui: "성공적으로 {player}에게 상점 GUI를 열었습니다" - open-fishing-bag: "성공적으로 {player}에게 낚시 가방을 열었습니다" - format-day: '일' - format-hour: '시간' - format-minute: '분' - format-second: '초' - -gui: - search: "검색" - select-file: '파일 선택' - select-item: "아이템 선택" - dupe-invalid-key: "● 중복되거나 유효하지 않은 키" - new-value: "새로운 값: " - temp-new-key: '새 키' - set-new-key: "새 키 설정" - edit-key: '{0} 수정' - delete-property: "<#00CED1>● 구성 제거" - click-confirm: "<#00FF7F> -> 클릭하여 확인" - invalid-number: "● 잘못된 숫자" - illegal-format: "● 잘못된 형식" - scroll-up: '● 위로 올리기' - scroll-down: '● 아래로 내리기' - cannot-scroll-up: "현재 최상단에 있습니다" - cannot-scroll-down: "더 내릴 수 없습니다" - next-page: '● 다음 페이지' - goto-next-page: '페이지 이동 {0} / {1}' - cannot-goto-next-page: '페이지가 더 없습니다' - previous-page: '● 이전 페이지' - goto-previous-page: '페이지 이동 {0} / {1}' - cannot-goto-previous-page: "뒤로 갈 수 없습니다" - back-to-parent-page: "<#FF8C00>상위 페이지로 돌아가기" - back-to-parent-folder: "<#FF8C00>상위 폴더로 돌아가기" - current-value: "현재 값: " - click-to-toggle: "<#00FF7F> -> 클릭하여 전환" - left-click-edit: "<#00FF7F> -> 왼쪽 클릭으로 수정" - right-click-reset: "<#FF6347> -> 오른쪽 클릭으로 초기화" - right-click-delete: "<#FF6347> -> 오른쪽 클릭으로 삭제" - right-click-cancel: "<#00CED1> -> 오른쪽 클릭으로 취소" - loot-show-in-finder: "<#5F9EA0>● 물고기 탐지기에 표시" - loot-score: "<#FF1493>● 점수" - loot-nick: "<#00FF00>● 이름" - loot-instant-game: "<#7B68EE>● 즉시 미니게임 시작" - loot-disable-statistics: "<#CD853F>● 통계 비활성화" - loot-disable-game: "<#8B4513>● 미니게임 비활성화" - item-amount: "<#1E90FF>● 수량" - item-custom-model-data: "<#FFC0CB>● Custom Model Data" - item-display-name: "<#FAFAD2>● 표시 이름" - item-custom-durability: "<#1E90FF>● 커스텀 내구도" - item-enchantment: "<#8A2BE2>● 마법 부여" - item-head64: "<#2E8B57>● Head64" - item-item-flag: "<#E6E6FA>● 아이템 플래그" - item-lore: "<#FA8072>● 설명" - item-material: "<#FF00FF>● 재료" - item-nbt: "<#FA8072>● NBT" - item-prevent-grab: "<#FF4500>● 잡기 방지" - item-price: "<#FFD700>● 가격" - item-price-base: " - 기본: " - item-price-bonus: " - 추가: " - item-random-durability: "<#FFFF00>● 무작위 내구도" - item-size: "<#FFF0F5>● 크기" - item-stackable: "<#9370DB>● 겹침 가능 여부" - item-stored-enchantment: "<#9370DB>● 저장된 마법 부여" - item-tag: "<#2E8B57>● 태그" - item-unbreakable: "<#C0C0C0>● 부서지지 않음" - page-amount-title: "수량 수정" - page-model-data-title: "CustomModelData 수정" - page-display-name-title: "표시 이름 수정" - page-new-display-name: "새 이름" - page-custom-durability-title: "커스텀 내구도 수정" - page-stored-enchantment-title: "저장된 마법 부여 수정" - page-enchantment-title: "마법 부여 수정" - page-select-one-enchantment: "한개의 마법 부여를 선택하세요" - page-add-new-enchantment: "[+] 새 마법 부여 추가" - page-item-flag-title: "아이템 플래그 수정" - page-lore-title: "아이템 설명 수정" - page-add-new-lore: "[+] 새 줄 추가" - page-select-one-lore: "줄 하나를 선택하세요" - page-material-title: "재료 수정" - page-nbt-compound-key-title: "컴파운드 키 수정" - page-nbt-list-key-title: "목록 키 수정" - page-nbt-key-title: "키 수정" - page-nbt-invalid-key: "잘못된 키" - page-nbt-add-new-compound: "[+] 새 컴파운드 추가" - page-nbt-add-new-list: "[+] 새 목록 추가" - page-nbt-add-new-value: "[+] 새 값 추가" - page-add-new-key: "[+] 새 키 추가" - page-nbt-preview: "● NBT 미리 보기" - page-nbt-back-to-compound: "상위 컴파운드로 돌아가기" - page-nbt-set-value-title: "값 설정" - page-nbt-edit-title: "NBT 수정" - page-nick-title: "별명 수정" - page-new-nick: "새 별명" - page-price-title: "가격 수정" - page-base-price: "기본" - page-base-bonus: "추가" - page-score-title: "점수 수정" - page-size-title: "크기 수정" - page-size-min: "최소" - page-size-max: "최대" - page-size-max-no-less-min: "● 최댓값은 최솟값보다 커야 합니다" \ No newline at end of file diff --git a/plugin/src/main/resources/messages/zh_cn.yml b/plugin/src/main/resources/messages/zh_cn.yml deleted file mode 100644 index 9f8a39ba..00000000 --- a/plugin/src/main/resources/messages/zh_cn.yml +++ /dev/null @@ -1,125 +0,0 @@ -# Don't change this -config-version: '31' - -messages: - prefix: '[CustomFishing] ' - reload: '重载完成. 耗时 {time}ms.' - item-not-exist: '物品不存在.' - give-item: '给予玩家 {player} {amount}x {item}.' - get-item: '获得 {amount}x {item}.' - possible-loots: '可能的战利品: ' - split-char: ', ' - competition-not-exist: '比赛 {id} 不存在.' - no-competition-ongoing: "没有正在进行的比赛." - stop-competition: '停止了当前的比赛.' - end-competition: '结束了当前的比赛.' - no-score: '无比分' - no-player: '无选手' - no-rank: '无排名' - goal-catch-amount: '捕鱼总数' - goal-max-size: '最大尺寸' - goal-total-size: '捕鱼总尺寸' - goal-total-score: '捕鱼总分' - unsafe-modification: "你不能修改一个正在其他子服游玩的玩家钓鱼背包." - never-played: "此玩家从未玩过服务器." - data-not-loaded: "数据未能正常加载. 请尝试切换子服或重进. 如果问题依然存在请联系服务器管理员." - open-market-gui: "为玩家 {player} 打开了市场" - open-fishing-bag: "为玩家 {player} 打开了钓鱼背包" - format-day: '天' - format-hour: '小时' - format-minute: '分' - format-second: '秒' - -gui: - search: 搜索 - select-file: 选择文件 - select-item-to-edit: 选择要编辑的物品 - dupe-invalid-key: ● 重复或无效的键 - new-value: '新值: ' - temp-new-key: '新键' - set-new-key: '设置新键' - edit-key: '修改 {0}' - delete-property: <#00CED1>● 删除属性 - click-confirm: <#00FF7F> -> 点击确认 - invalid-number: ● 无效的数字 - illegal-format: ● 非法的格式 - scroll-up: ● 向上翻 - scroll-down: ● 向下翻 - cannot-scroll-up: 你已经到顶了 - cannot-scroll-down: 你不能再往下了 - next-page: ● 下一页 - goto-next-page: 前往 {0} / {1} - cannot-goto-next-page: 没有更多页了 - previous-page: ● 上一页 - goto-previous-page: 前往 {0} / {1} - cannot-goto-previous-page: 已经到达第一页了 - back-to-parent-page: <#FF8C00>返回上一界面 - back-to-parent-folder: <#FF8C00>返回父文件夹 - current-value: '当前值: ' - click-to-toggle: <#00FF7F> -> 点击切换 - left-click-edit: <#00FF7F> -> 左键编辑 - right-click-reset: <#FF6347> -> 右键重置 - right-click-delete: <#FF6347> -> 右键删除 - right-click-cancel: <#00CED1> -> 右键取消 - loot-show-in-finder: <#5F9EA0>● 在找鱼器中可见 - loot-score: <#FF1493>● 比赛分数 - loot-nick: <#00FF00>● 昵称 - loot-instant-game: <#7B68EE>● 咬钩立即游戏 - loot-disable-statistics: <#CD853F>● 禁用统计数据 - loot-disable-game: <#8B4513>● 禁用游戏 - item-amount: <#1E90FF>● 数量 - item-custom-model-data: <#FFC0CB>● 自定义模型值 - item-display-name: <#FAFAD2>● 名称 - item-custom-durability: <#1E90FF>● 自定义耐久度 - item-enchantment: <#8A2BE2>● 附魔 - item-head64: <#2E8B57>● 头颅base64 - item-item-flag: <#E6E6FA>● 物品标签 - item-lore: <#FA8072>● 描述 - item-material: <#FF00FF>● 材质 - item-nbt: <#FA8072>● NBT - item-prevent-grab: <#FF4500>● 防止抢夺 - item-price: <#FFD700>● 价格 - item-price-base: ' - 基础: ' - item-price-bonus: ' - 尺寸增益: ' - item-random-durability: <#FFFF00>● 随机耐久 - item-size: <#FFF0F5>● 尺寸 - item-stackable: <#9370DB>● 是否可以堆叠 - item-stored-enchantment: <#9370DB>● 存储附魔 - item-tag: <#2E8B57>● 启用CustomFishing标签 - item-unbreakable: <#C0C0C0>● 不可破坏 - page-amount-title: 修改数量 - page-model-data-title: 修改自定义模型值 - page-display-name-title: 修改名称 - page-new-display-name: 新名称 - page-custom-durability-title: 修改自定义耐久度 - page-stored-enchantment-title: 修改存储附魔 - page-enchantment-title: 修改附魔 - page-select-one-enchantment: 选择一个附魔 - page-add-new-enchantment: [+] 新增一个附魔 - page-item-flag-title: 修改物品标签 - page-lore-title: 修改描述 - page-add-new-lore: [+] 新增一行描述 - page-select-one-lore: 选择一行描述 - page-material-title: 修改材质 - page-nbt-compound-key-title: 修改复合键名 - page-nbt-list-key-title: 修改列表名 - page-nbt-key-title: 修改键 - page-nbt-invalid-key: 无效的键 - page-nbt-add-new-compound: [+] 新增一个复合NBT - page-nbt-add-new-list: [+] 新增一个列表 - page-nbt-add-new-value: [+] 新增一个值 - page-add-new-key: [+] 新增一个键 - page-nbt-preview: ● NBT 预览 - page-nbt-back-to-compound: 返回父复合NBT - page-nbt-set-value-title: 设置值 - page-nbt-edit-title: 修改NBT - page-nick-title: 修改昵称 - page-new-nick: 新昵称 - page-price-title: 修改价格 - page-base-price: 基础 - page-base-bonus: 尺寸增益 - page-score-title: 修改分数 - page-size-title: 修改尺寸 - page-size-min: 最小值 - page-size-max: 最大值 - page-size-max-no-less-min: ● 最大值必须大于最小值 diff --git a/settings.gradle.kts b/settings.gradle.kts index d2bc3642..24d8ad1d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,5 @@ rootProject.name = "CustomFishing" include("api") -include("plugin") +include("common") +include("core") +include("compatibility")