diff --git a/build.gradle.kts b/build.gradle.kts index fc654d2..ab22355 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,7 @@ plugins { kotlin("jvm") version "1.9.24" java + id("org.jetbrains.dokka").version("1.9.20") id("com.github.johnrengelman.shadow").version("7.1.2") } diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilder.java b/src/main/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilder.java new file mode 100644 index 0000000..74e8118 --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilder.java @@ -0,0 +1,205 @@ +package xyz.alexcrea.cuanvil.api; + +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe; + +/** + * A Builder for custom craft using anvil. + */ +@SuppressWarnings("unused") +public class AnvilRecipeBuilder { + + private @NotNull String name; + private boolean exactCount; + + private int xpCostPerCraft; + + private @Nullable ItemStack leftItem; + private @Nullable ItemStack rightItem; + private @Nullable ItemStack resultItem; + + /** + * Instantiates a new Anvil recipe builder. + * exact count default to true. + * xp cost per craft default to 1. + * + * @param name The recipe name + */ + public AnvilRecipeBuilder(@NotNull String name) { + this.name = name; + + this.exactCount = true; + this.xpCostPerCraft = 1; + + this.leftItem = null; + this.rightItem = null; + this.resultItem = null; + } + + /** + * Gets the recipe name. + * + * @return This recipe builder instance. + */ + @NotNull + public String getName() { + return name; + } + + /** + * Sets the recipe name. + * + * @param name The recipe name + * @return This recipe builder instance. + */ + public AnvilRecipeBuilder setName(String name) { + this.name = name; + return this; + } + + /** + * Get if the recipe is exact count. + *

+ * Exact count mean the recipe can only be crafted 1 by 1. + * If set to false, then it will craft as much as possible in 1 go and will keep unused material onto the anvil inventory. + * + * @return If the recipe is exact count. + */ + public boolean isExactCount() { + return exactCount; + } + + /** + * Sets if the recipe is exact count. + *

+ * Exact count mean the recipe can only be crafted 1 by 1. + * If set to false, then it will craft as much as possible in 1 go and will keep unused material onto the anvil inventory. + * + * @param exactCount If the recipe is exact count + * @return This recipe builder instance. + */ + public AnvilRecipeBuilder setExactCount(boolean exactCount) { + this.exactCount = exactCount; + return this; + } + + /** + * Get the xp level cost per craft. + * + * @return The xp level cost per craft + */ + public int getXpCostPerCraft() { + return xpCostPerCraft; + } + + /** + * Sets the xp level cost per craft. + * + * @param xpCostPerCraft The xp level cost per craft + * @return This recipe builder instance. + */ + public AnvilRecipeBuilder setXpCostPerCraft(int xpCostPerCraft) { + this.xpCostPerCraft = xpCostPerCraft; + return this; + } + + /** + * Get the left item of the recipe. + * If null (default) then the recipe will not be able to be registered. + * + * @return The left item + */ + @Nullable + public ItemStack getLeftItem() { + return leftItem; + } + + /** + * Set the left item. + * If null (default) then the recipe will not be able to be registered. + * + * @param leftItem the left item + * @return This recipe builder instance. + */ + public AnvilRecipeBuilder setLeftItem(ItemStack leftItem) { + this.leftItem = leftItem; + return this; + } + + /** + * Get the recipe right item. + * null on default new instance. + * + * @return The right item + */ + @Nullable + public ItemStack getRightItem() { + return rightItem; + } + + /** + * Set the recipe right item. + * null on default new instance. + * + * @param rightItem the right item + * @return This recipe builder instance. + */ + public AnvilRecipeBuilder setRightItem(ItemStack rightItem) { + this.rightItem = rightItem; + return this; + } + + /** + * Get the recipe result item. + * If null (default) then the recipe will not be able to be registered. + * + * @return The result item + */ + @Nullable + public ItemStack getResultItem() { + return resultItem; + } + + /** + * Set the recipe result item. + * If null (default) then the recipe will not be able to be registered. + * + * @param resultItem The result item + * @return This recipe builder instance. + */ + public AnvilRecipeBuilder setResultItem(ItemStack resultItem) { + this.resultItem = resultItem; + return this; + } + + /** + * Build the anvil custom recipe. + * Should probably use {@link #registerIfAbsent() registerIfAbsent} or {@link ConflictAPI#addConflict(ConflictBuilder) addConflict}. + * + * @return A new anvil custom recipe base on this builder. + */ + @Nullable // null if missing argument + public AnvilCustomRecipe build() { + if(leftItem == null || rightItem == null) return null; + + return new AnvilCustomRecipe( + this.name, + this.exactCount, + this.xpCostPerCraft, + this.leftItem, this.rightItem, this.resultItem + ); + } + + /** + * Register this recipe if absent. + * Equivalent to {@link ConflictAPI#addConflict(ConflictBuilder)} + * + * @return True if successful. + */ + public boolean registerIfAbsent(){ + return CustomAnvilRecipeApi.addRecipe(this); + } + +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/ConflictAPI.java b/src/main/java/xyz/alexcrea/cuanvil/api/ConflictAPI.java new file mode 100644 index 0000000..28fccd2 --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/api/ConflictAPI.java @@ -0,0 +1,170 @@ +package xyz.alexcrea.cuanvil.api; + +import io.delilaheve.CustomAnvil; +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.group.EnchantConflictGroup; +import xyz.alexcrea.cuanvil.gui.config.global.EnchantConflictGui; + +import java.util.*; + +/** + * Custom Anvil api for conflict registry. + */ +@SuppressWarnings("unused") +public class ConflictAPI { + + private ConflictAPI() {} + + private static int saveChangeTask = -1; + private static int reloadChangeTask = -1; + + /** + * Write and add a conflict. + * Will not write the conflict if it already exists. + * + * @param builder The conflict builder to be based on + * @return True if successful. + */ + public static boolean addConflict(@NotNull ConflictBuilder builder){ + FileConfiguration config = ConfigHolder.CONFLICT_HOLDER.getConfig(); + if(config.contains(builder.getName())) return false; + + if(!writeConflict(builder, false)) return false; + + EnchantConflictGroup conflict = builder.build(); + + // Register conflict + ConfigHolder.CONFLICT_HOLDER.getConflictManager().addConflict(conflict); + + // Add conflict to gui + EnchantConflictGui.INSTANCE.updateValueForGeneric(conflict, true); + + return true; + } + + /** + * Write a conflict to the config file and plan an update of conflicts. + *

+ * You may want to use {@link #addConflict(ConflictBuilder)} instead as it is more performance in most case as this function will reload every conflict. + * + * @param builder The builder + * @return True if successful. + */ + public static boolean writeConflict(@NotNull ConflictBuilder builder){ + return writeConflict(builder, true); + } + + /** + * Write a conflict to the config file. + *

+ * You should use {@link #addConflict(ConflictBuilder)} or {@link #writeConflict(ConflictBuilder)} instead + * + * @param builder The builder + * @param updatePlanned If we should plan a global update for conflicts + * @return True if successful. + */ + public static boolean writeConflict(@NotNull ConflictBuilder builder, boolean updatePlanned){ + FileConfiguration config = ConfigHolder.CONFLICT_HOLDER.getConfig(); + + String name = builder.getName(); + if(name.contains(".")) { + CustomAnvil.instance.getLogger().warning("Conflict " + name +" contain \".\" in its name but should not. this conflict is ignored."); + logConflictOrigin(builder); + return false; + } + + String basePath = name + "."; + + List enchantments = extractEnchantments(builder); + List excludedGroups = new ArrayList<>(builder.getExcludedGroupNames()); + if(!enchantments.isEmpty()) config.set(basePath + "enchantments", enchantments); + if(!excludedGroups.isEmpty()) config.set(basePath + "notAffectedGroups", excludedGroups); + if(builder.getMaxBeforeConflict() > 0) config.set(basePath + "maxEnchantmentBeforeConflict", builder.getMaxBeforeConflict()); + + + prepareSaveTask(); + if(updatePlanned) prepareUpdateTask(); + + return true; + } + + /** + * Extract every enchantment names from a builder. + * @param builder The builder storing the enchantments + * @return Builder's stored enchantment. + */ + @NotNull + private static List extractEnchantments(@NotNull ConflictBuilder builder){ + List result = new ArrayList<>(builder.getEnchantmentNames()); + for (NamespacedKey enchantmentKey : builder.getEnchantmentKeys()) { + result.add(enchantmentKey.getKey()); + } + + return result; + } + + /** + * Remove a conflict. + * + * @param conflict The conflict to remove + * @return True if successful. + */ + public static boolean removeConflict(@NotNull EnchantConflictGroup conflict){ + // Remove from registry + ConfigHolder.CONFLICT_HOLDER.getConflictManager().removeConflict(conflict); + + // Write as null and save to file + ConfigHolder.CONFLICT_HOLDER.getConfig().set(conflict.getName(), null); + prepareSaveTask(); + + // Remove from gui + EnchantConflictGui.INSTANCE.removeGeneric(conflict); + + return true; + } + + /** + * Prepare a task to save conflict configuration. + */ + private static void prepareSaveTask() { + if(saveChangeTask != -1) return; + + saveChangeTask = Bukkit.getScheduler().scheduleSyncDelayedTask(CustomAnvil.instance, ()->{ + ConfigHolder.CONFLICT_HOLDER.saveToDisk(true); + saveChangeTask = -1; + }, 0L); + } + + /** + * Prepare a task to reload every conflict. + */ + private static void prepareUpdateTask() { + if(reloadChangeTask != -1) return; + + reloadChangeTask = Bukkit.getScheduler().scheduleSyncDelayedTask(CustomAnvil.instance, ()->{ + ConfigHolder.CONFLICT_HOLDER.reload(); + EnchantConflictGui.INSTANCE.reloadValues(); + reloadChangeTask = -1; + }, 0L); + + } + + static void logConflictOrigin(@NotNull ConflictBuilder builder){ + CustomAnvil.instance.getLogger().warning("Conflict " + builder.getName() +" came from " + builder.getSourceName() + "."); + } + + /** + * Get every registered conflict. + * @return An immutable collection of conflict. + */ + @NotNull + public static List getRegisteredConflict(){ + List mutableList = ConfigHolder.CONFLICT_HOLDER.getConflictManager().getConflictList(); + return Collections.unmodifiableList(mutableList); + } + +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/ConflictBuilder.java b/src/main/java/xyz/alexcrea/cuanvil/api/ConflictBuilder.java new file mode 100644 index 0000000..3e63b36 --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/api/ConflictBuilder.java @@ -0,0 +1,426 @@ +package xyz.alexcrea.cuanvil.api; + +import io.delilaheve.CustomAnvil; +import org.bukkit.NamespacedKey; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.enchant.CAEnchantment; +import xyz.alexcrea.cuanvil.group.*; + +import java.util.HashSet; +import java.util.Set; + +/** + * A Builder for material conflict. + */ +@SuppressWarnings("unused") +public class ConflictBuilder { + + private final @Nullable Plugin source; + private @NotNull String name; + + private final @NotNull Set enchantmentNames; + private final @NotNull Set enchantmentKeys; + + private final @NotNull Set excludedGroupNames; + + private int maxBeforeConflict; + + /** + * Instantiates a new Conflict builder. + * + * @param name The conflict name + * @param maxBeforeConflict Maximum number of conflicting enchantment before conflict is active + * @param source The conflict source + */ + public ConflictBuilder(@NotNull String name, int maxBeforeConflict, @Nullable Plugin source){ + this.source = source; + this.name = name; + + this.enchantmentNames = new HashSet<>(); + this.enchantmentKeys = new HashSet<>(); + + this.excludedGroupNames = new HashSet<>(); + + this.maxBeforeConflict = maxBeforeConflict; + } + + /** + * Instantiates a new Conflict builder. + * + * @param name The conflict name + * @param source The conflict source + */ + public ConflictBuilder(@NotNull String name, @Nullable Plugin source){ + this(name, 0, source); + } + + /** + * Instantiates a new Conflict builder. + * + * @param name The conflict name + */ + public ConflictBuilder(@NotNull String name){ + this(name, null); + } + + /** + * Gets conflict source. + * + * @return The conflict source. + */ + @Nullable + public Plugin getSource() { + return source; + } + + /** + * Gets conflict source name. + * + * @return The conflict source name. + */ + @NotNull + public String getSourceName() { + if(source == null) return "an unknown source"; + + return source.getName(); + } + + /** + * Gets conflict name. + * + * @return The conflict name. + */ + @NotNull + public String getName() { + return name; + } + + /** + * Gets stored conflicting enchantment names. + * + * @return The stored enchantment names. + */ + @NotNull + public Set getEnchantmentNames() { + return enchantmentNames; + } + + /** + * Gets stored conflicting enchantment keys. + * + * @return The stored enchantment keys. + */ + @NotNull + public Set getEnchantmentKeys() { + return enchantmentKeys; + } + + /** + * Gets stored excluded group names. + * + * @return The stored group names. + */ + @NotNull + public Set getExcludedGroupNames() { + return excludedGroupNames; + } + + /** + * Gets maximum number of conflicting enchantment before conflict is active. + *

+ * This value represent how many enchantment contained on this conflict can be applied to before conflict is considered active. + * That mean new enchantment will not be able to be added to the item and present enchantment will not have its level upgraded. + *

+ * In vanilla. material restriction have this value set to 0 and enchantment conflict set to 1. + * + * @return the max number of conflicting enchantment before conflict. 0 by default. + */ + public int getMaxBeforeConflict() { + return maxBeforeConflict; + } + + /** + * Sets conflict name. + * + * @param name The name + * @return This conflict builder instance. + */ + public ConflictBuilder setName(String name) { + this.name = name; + return this; + } + + /** + * Sets maximum number of conflicting enchantment before conflict is active. + *

+ * This value represent how many enchantment contained on this conflict can be applied to before conflict is considered active. + * That mean new enchantment will not be able to be added to the item and present enchantment will not have its level upgraded. + *

+ * In vanilla. material restriction have this value set to 0 and enchantment conflict set to 1. + * + * @param maxBeforeConflict The max before conflict + * @return This conflict builder instance. + */ + public ConflictBuilder setMaxBeforeConflict(int maxBeforeConflict) { + this.maxBeforeConflict = maxBeforeConflict; + return this; + } + + /** + * Add a conflicting enchantment by name. + * + * @param enchantmentName The enchantment name + * @return This conflict builder instance. + */ + @NotNull + public ConflictBuilder addEnchantment(@NotNull String enchantmentName){ + enchantmentNames.add(enchantmentName); + return this; + } + + /** + * Add a conflicting enchantment by key. + * + * @param enchantmentKey The enchantment key + * @return This conflict builder instance. + */ + @NotNull + public ConflictBuilder addEnchantment(@NotNull NamespacedKey enchantmentKey){ + enchantmentKeys.add(enchantmentKey); + return this; + } + + /** + * Add a conflicting enchantment by instance. + * + * @param enchantment The enchantment + * @return This conflict builder instance. + */ + @NotNull + public ConflictBuilder addEnchantment(@NotNull CAEnchantment enchantment){ + addEnchantment(enchantment.getKey()); + return this; + } + + /** + * Remove conflicting enchantment by name. + * + * @param enchantmentName The enchantment name + * @return This conflict builder instance. + */ + @NotNull + public ConflictBuilder removeEnchantment(@NotNull String enchantmentName){ + enchantmentNames.remove(enchantmentName); + return this; + } + + /** + * Remove conflicting enchantment by key. + * + * @param enchantmentKey The enchantment key + * @return This conflict builder instance. + */ + @NotNull + public ConflictBuilder removeEnchantment(@NotNull NamespacedKey enchantmentKey){ + enchantmentKeys.remove(enchantmentKey); + return removeEnchantment(enchantmentKey.getKey()); + } + + /** + * Remove enchantment by instance. + * + * @param enchantment The enchantment + * @return This conflict builder instance. + */ + @NotNull + public ConflictBuilder removeEnchantment(@NotNull CAEnchantment enchantment){ + return removeEnchantment(enchantment.getKey()); + } + + /** + * Add an excluded group by name. + *

+ * If left item of an anvil craft is included on one of the excluded group it will ignore this conflict. + *

+ * This allows to create conflict only for some item. Material restriction can be written like that. + *

+ * For example: If we exclude a material group containing every pickaxe and add efficiency enchantment + * with {@link #setMaxBeforeConflict(int) maxBeforeConflict} set to 0. + * Then only pickaxe will be able to have efficiency. + * + * @param groupName The group name + * @return This conflict builder instance. + */ + @NotNull + public ConflictBuilder addExcludedGroup(@NotNull String groupName){ + excludedGroupNames.add(groupName); + return this; + } + + /** + * Add an excluded group by instance. + *

+ * If left item of an anvil craft is included on one of the excluded group it will ignore this conflict. + *

+ * This allows to create conflict only for some item. Material restriction can be written like that. + *

+ * For example: If we exclude a material group containing every pickaxe and add efficiency enchantment + * with {@link #setMaxBeforeConflict(int) maxBeforeConflict} set to 0. + * Then only pickaxe will be able to have efficiency. + * + * @param group The group + * @return this conflict builder instance. + */ + @NotNull + public ConflictBuilder addExcludedGroup(@NotNull AbstractMaterialGroup group){ + return addExcludedGroup(group.getName()); + } + + /** + * Remove an excluded group by name. + *

+ * If left item of an anvil craft is included on one of the excluded group it will ignore this conflict. + *

+ * This allows to create conflict only for some item. Material restriction can be written like that. + *

+ * For example: If we exclude a material group containing every pickaxe and add efficiency enchantment + * with {@link #setMaxBeforeConflict(int) maxBeforeConflict} set to 0. + * Then only pickaxe will be able to have efficiency. + * + * @param groupName The group name + * @return This conflict builder instance. + */ + @NotNull + public ConflictBuilder removeExcludedGroup(@NotNull String groupName){ + excludedGroupNames.remove(groupName); + return this; + } + + /** + * Remove an excluded group by instance. + *

+ * If left item of an anvil craft is included on one of the excluded group it will ignore this conflict. + *

+ * This allows to create conflict only for some item. Material restriction can be written like that. + *

+ * For example: If we exclude a material group containing every pickaxe and add efficiency enchantment + * with {@link #setMaxBeforeConflict(int) maxBeforeConflict} set to 0. + * Then only pickaxe will be able to have efficiency. + * + * @param group The group + * @return This conflict builder instance. + */ + @NotNull + public ConflictBuilder removeExcludedGroup(@NotNull AbstractMaterialGroup group){ + return removeExcludedGroup(group.getName()); + } + + /** + * Copy this conflict builder. + * + * @return A copy of this conflict builder. + */ + @NotNull + public ConflictBuilder copy() { + ConflictBuilder copy = new ConflictBuilder(this.name, this.source); + + setMaxBeforeConflict(this.maxBeforeConflict); + + // Set Enchantments + for (NamespacedKey key : this.enchantmentKeys) { + copy.addEnchantment(key); + } + for (String enchantName : this.enchantmentNames) { + copy.addEnchantment(enchantName); + } + + // Set Groups + for (String groupName : this.excludedGroupNames) { + copy.addExcludedGroup(groupName); + } + + return copy; + } + /** + * Build a new Enchant conflict group by this builder. + * @return An Enchant conflict group with this builder parameters. + */ + public EnchantConflictGroup build(){ + AbstractMaterialGroup materials = extractGroups(); + EnchantConflictGroup conflict = new EnchantConflictGroup(getName(), materials, getMaxBeforeConflict()); + appendEnchantments(conflict); + + return conflict; + } + + /** + * Register this conflict if not yet registered. + * Equivalent to {@link ConflictAPI#addConflict(ConflictBuilder)} + * @return True if successful. + */ + public boolean registerIfAbsent(){ + return ConflictAPI.addConflict(this); + } + + /** + * Append builders stored enchantments into conflict. + * + * @param conflict The conflict target + */ + protected void appendEnchantments(@NotNull EnchantConflictGroup conflict){ + for (String enchantmentName : getEnchantmentNames()){ + if(appendEnchantment(conflict, EnchantmentApi.getByName(enchantmentName))){ + CustomAnvil.instance.getLogger().warning("Could not find enchantment " + enchantmentName + " for conflict " + getName()); + ConflictAPI.logConflictOrigin(this); + } + } + for (NamespacedKey enchantmentKey : getEnchantmentKeys()){ + if(!appendEnchantment(conflict, EnchantmentApi.getByKey(enchantmentKey))){ + CustomAnvil.instance.getLogger().warning("Could not find enchantment " + enchantmentKey + " for conflict " + getName()); + ConflictAPI.logConflictOrigin(this); + } + } + } + + /** + * Append an enchantment. + * + * @param conflict The conflict target + * @param enchantment The enchantment + * @return True if successful. + */ + protected static boolean appendEnchantment(@NotNull EnchantConflictGroup conflict, @Nullable CAEnchantment enchantment){ + if(enchantment == null) + return false; + conflict.addEnchantment(enchantment); + return true; + } + + /** + * Extract group abstract material group. + * + * @return The abstract material group from the builder. + */ + protected AbstractMaterialGroup extractGroups(){ + ItemGroupManager itemGroupManager = ConfigHolder.ITEM_GROUP_HOLDER.getItemGroupsManager(); + IncludeGroup group = new IncludeGroup(EnchantConflictManager.DEFAULT_GROUP_NAME); + + for (String groupName : getExcludedGroupNames()) { + AbstractMaterialGroup materialGroup = itemGroupManager.get(groupName); + + if(materialGroup == null){ + CustomAnvil.instance.getLogger().warning("Material group " + groupName + " do not exist but is ask by conflict " + getName()); + ConflictAPI.logConflictOrigin(this); + continue; + } + + group.addToPolicy(materialGroup); + } + + return group; + } + +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/CustomAnvilRecipeApi.java b/src/main/java/xyz/alexcrea/cuanvil/api/CustomAnvilRecipeApi.java new file mode 100644 index 0000000..69bfad2 --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/api/CustomAnvilRecipeApi.java @@ -0,0 +1,108 @@ +package xyz.alexcrea.cuanvil.api; + +import io.delilaheve.CustomAnvil; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.gui.config.global.CustomRecipeConfigGui; +import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe; + +import java.util.Collections; +import java.util.List; + +/** + * Custom Anvil api for custom anvil recipes. + */ +@SuppressWarnings("unused") +public class CustomAnvilRecipeApi { + + private CustomAnvilRecipeApi(){} + + private static int saveChangeTask = -1; + + /** + * Write and add a custom anvil recipe. + * Will not write the recipe if it already exists. + * + * @param builder The recipe builder to be based on + * @return True if successful. + */ + public static boolean addRecipe(@NotNull AnvilRecipeBuilder builder){ + FileConfiguration config = ConfigHolder.CUSTOM_RECIPE_HOLDER.getConfig(); + String name = builder.getName(); + + if(config.contains(builder.getName())) return false; + if(builder.getName().contains(".")) { + CustomAnvil.instance.getLogger().warning("Custom anvil recipe " + name + " contain \".\" in its name but should not. this recipe is ignored."); + return false; + } + + AnvilCustomRecipe recipe = builder.build(); + if(recipe == null){ + CustomAnvil.instance.getLogger().warning("Custom anvil recipe " + name + " could not be parsed."); + if(builder.getLeftItem() == null){ + CustomAnvil.instance.getLogger().warning("It look like left item of the recipe is null."); + } + if(builder.getResultItem() == null){ + CustomAnvil.instance.getLogger().warning("It look like result item of the recipe is null."); + } + return false; + } + + // Add to registry + ConfigHolder.CUSTOM_RECIPE_HOLDER.getRecipeManager().cleanAddNew(recipe); + + // Save to file + recipe.saveToFile(false, false); + prepareSaveTask(); + + // Add from gui + CustomRecipeConfigGui.INSTANCE.updateValueForGeneric(recipe, true); + + return true; + } + + /** + * Remove a custom anvil recipe. + * + * @param recipe The recipe to remove + * @return True if successful. + */ + public static boolean removeRecipe(@NotNull AnvilCustomRecipe recipe){ + // Remove from registry + ConfigHolder.CUSTOM_RECIPE_HOLDER.getRecipeManager().cleanRemove(recipe); + + // Write as null and save to file + ConfigHolder.CUSTOM_RECIPE_HOLDER.getConfig().set(recipe.getName(), null); + prepareSaveTask(); + + // Remove from gui + CustomRecipeConfigGui.INSTANCE.removeGeneric(recipe); + + return true; + } + + /** + * Prepare a task to save custom recipe configuration. + */ + private static void prepareSaveTask() { + if(saveChangeTask != -1) return; + + saveChangeTask = Bukkit.getScheduler().scheduleSyncDelayedTask(CustomAnvil.instance, ()->{ + ConfigHolder.CONFLICT_HOLDER.saveToDisk(true); + saveChangeTask = -1; + }, 0L); + } + + /** + * Get every registered recipes. + * @return An immutable collection of recipes. + */ + @NotNull + public static List getRegisteredRecipes(){ + List mutableList = ConfigHolder.CUSTOM_RECIPE_HOLDER.getRecipeManager().getRecipeList(); + return Collections.unmodifiableList(mutableList); + } + +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/EnchantmentApi.java b/src/main/java/xyz/alexcrea/cuanvil/api/EnchantmentApi.java new file mode 100644 index 0000000..b950938 --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/api/EnchantmentApi.java @@ -0,0 +1,207 @@ +package xyz.alexcrea.cuanvil.api; + +import io.delilaheve.CustomAnvil; +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.enchantments.Enchantment; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.enchant.CAEnchantment; +import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry; +import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity; +import xyz.alexcrea.cuanvil.enchant.bulk.BulkCleanEnchantOperation; +import xyz.alexcrea.cuanvil.enchant.bulk.BulkGetEnchantOperation; +import xyz.alexcrea.cuanvil.enchant.wrapped.CAVanillaEnchantment; +import xyz.alexcrea.cuanvil.gui.config.global.EnchantCostConfigGui; +import xyz.alexcrea.cuanvil.gui.config.global.EnchantLimitConfigGui; + +import java.util.Collections; +import java.util.Map; + +/** + * Custom Anvil api for enchantment registry. + */ +@SuppressWarnings("unused") +public class EnchantmentApi { + + private static int saveChangeTask = -1; + + private EnchantmentApi() {} + + /** + * Register an enchantment. + * + * @param enchantment The enchantment to register + * @return True if successful. + */ + public static boolean registerEnchantment(@NotNull CAEnchantment enchantment){ + if(!CAEnchantmentRegistry.getInstance().register(enchantment)) return false; + + // Add enchantment to gui. + if(EnchantCostConfigGui.getInstance() != null){ + EnchantCostConfigGui.getInstance().updateValueForGeneric(enchantment, true); + } + if(EnchantLimitConfigGui.getInstance() != null){ + EnchantLimitConfigGui.getInstance().updateValueForGeneric(enchantment, true); + } + + // Write default if do not exist + writeDefaultConfig(enchantment, false); + + return true; + } + + /** + * Register an enchantment by minecraft registered enchantment instance. + * + * @param enchantment The enchantment to register + * @param defaultRarity The default rarity of the provided enchantment + * @return True if successful. + */ + public static boolean registerEnchantment(@NotNull Enchantment enchantment, @Nullable EnchantmentRarity defaultRarity){ + if(defaultRarity == null) + return registerEnchantment(new CAVanillaEnchantment(enchantment)); + + return registerEnchantment(new CAVanillaEnchantment(enchantment, defaultRarity)); + } + + /** + * Register an enchantment by minecraft registered enchantment instance. + *

+ * Please note that this function assume the provided enchantment is registered into minecraft registry. + * + * @param enchantment The enchantment to register + * @return True if successful. + */ + public static boolean registerEnchantment(@NotNull Enchantment enchantment){ + return registerEnchantment(new CAVanillaEnchantment(enchantment)); + } + + /** + * Unregister an enchantment. + * + * @param enchantment The enchantment to unregister + * @return True if successful. + */ + public static boolean unregisterEnchantment(@Nullable CAEnchantment enchantment){ + // Remove from gui + if(EnchantCostConfigGui.getInstance() != null){ + EnchantCostConfigGui.getInstance().removeGeneric(enchantment); + } + if(EnchantLimitConfigGui.getInstance() != null){ + EnchantLimitConfigGui.getInstance().removeGeneric(enchantment); + } + + return CAEnchantmentRegistry.getInstance().unregister(enchantment); + } + + /** + * Unregister an enchantment by its key. + * + * @param key The enchantment key to unregister + * @return True if successful. + */ + public static boolean unregisterEnchantment(@NotNull NamespacedKey key){ + CAEnchantment enchantment = CAEnchantmentRegistry.getInstance().getByKey(key); + return unregisterEnchantment(enchantment); + } + + /** + * Unregister an enchantment by his bukkit enchantment. + * + * @param enchantment The enchantment to unregister + * @return True if successful. + */ + public static boolean unregisterEnchantment(@NotNull Enchantment enchantment){ + return unregisterEnchantment(enchantment.getKey()); + } + + /** + * Get by key an enchantment. + * + * @param key The key used to fetch + * @return The custom anvil enchantment of this key. null if not found. + */ + @Nullable + public static CAEnchantment getByKey(@NotNull NamespacedKey key){ + return CAEnchantmentRegistry.getInstance().getByKey(key); + } + + /** + * Get by name an enchantment. + * + * @param name The name used to fetch + * @return The custom anvil enchantment of this name. null if not found. + */ + @Nullable + public static CAEnchantment getByName(@NotNull String name){ + return CAEnchantmentRegistry.getInstance().getByName(name); + } + + /** + * Get every registered custom anvil enchantments. + * @return An immutable map of enchantment key as map key and custom anvil enchantment as value. + */ + @NotNull + public static Map getRegisteredEnchantments(){ + return Collections.unmodifiableMap(CAEnchantmentRegistry.getInstance().registeredEnchantments()); + } + + /** + * Write the default level and rarity configuration of the enchantment. + * @param enchantment The enchantment to write default configuration + * @param override If it should override old configuration + * @return Return false if override is false and a configuration exist. true otherwise. + */ + public static boolean writeDefaultConfig(CAEnchantment enchantment, boolean override){ + FileConfiguration config = ConfigHolder.DEFAULT_CONFIG.getConfig(); + if(!override && config.contains(enchantment.getName())) return false; + + writeDefaultConfig(config, enchantment); + + prepareSaveTask(); + return true; + } + + + private static void writeDefaultConfig(FileConfiguration defaultConfig, CAEnchantment enchantment) { + defaultConfig.set("enchant_limits." + enchantment.getKey().getKey(), enchantment.defaultMaxLevel()); + + String basePath = "enchant_values." + enchantment.getKey().getKey(); + EnchantmentRarity rarity = enchantment.defaultRarity(); + + defaultConfig.set(basePath + ".item", rarity.getItemValue()); + defaultConfig.set(basePath + ".book", rarity.getBookValue()); + } + + /** + * Prepare a task to save custom recipe configuration. + */ + private static void prepareSaveTask() { + if(saveChangeTask != -1) return; + + saveChangeTask = Bukkit.getScheduler().scheduleSyncDelayedTask(CustomAnvil.instance, ()->{ + ConfigHolder.DEFAULT_CONFIG.saveToDisk(true); + saveChangeTask = -1; + }, 0L); + } + + /** + * Add a bulk get operator. + * @param operation An optimised get enchantments operation + */ + public static void addBulkGet(@NotNull BulkGetEnchantOperation operation){ + CAEnchantmentRegistry.getInstance().getOptimisedGetOperators().add(operation); + } + + /** + * Add a bulk clean operator. + * @param operation An optimised clean enchantments operation + */ + public static void addBulkClean(@NotNull BulkCleanEnchantOperation operation){ + CAEnchantmentRegistry.getInstance().getOptimisedCleanOperators().add(operation); + } + +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/MaterialGroupApi.java b/src/main/java/xyz/alexcrea/cuanvil/api/MaterialGroupApi.java new file mode 100644 index 0000000..b3283aa --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/api/MaterialGroupApi.java @@ -0,0 +1,205 @@ +package xyz.alexcrea.cuanvil.api; + +import io.delilaheve.CustomAnvil; +import io.delilaheve.util.ConfigOptions; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.group.AbstractMaterialGroup; +import xyz.alexcrea.cuanvil.group.ExcludeGroup; +import xyz.alexcrea.cuanvil.group.IncludeGroup; +import xyz.alexcrea.cuanvil.group.ItemGroupManager; +import xyz.alexcrea.cuanvil.gui.config.global.GroupConfigGui; + +import java.util.*; + +/** + * Custom Anvil api for material group registry. + */ +@SuppressWarnings("unused") +public class MaterialGroupApi { + + private MaterialGroupApi(){} + + private static int saveChangeTask = -1; + private static int reloadChangeTask = -1; + + /** + * Write and add a group. + * Will not write the group if it already exists. + * + * @param group the group to add + * @return true if successful. + */ + public static boolean addMaterialGroup(@NotNull AbstractMaterialGroup group){ + ItemGroupManager itemGroupManager = ConfigHolder.ITEM_GROUP_HOLDER.getItemGroupsManager(); + if(itemGroupManager.get(group.getName()) != null) return false; + itemGroupManager.getGroupMap().put(group.getName(), group); + + if(!writeMaterialGroup(group, false)) return false; + + if(group instanceof IncludeGroup includeGroup){ + GroupConfigGui.INSTANCE.updateValueForGeneric(includeGroup, true); + } + + if(ConfigOptions.INSTANCE.getVerboseDebugLog()){ + CustomAnvil.instance.getLogger().info("Registered group " + group.getName()); + } + + return true; + } + + /** + * Write a material group to the config file and plan an update of groups. + *

+ * You may want to use {@link #addMaterialGroup(AbstractMaterialGroup)} instead as it is more performance in most case as this function will reload every conflict. + * + * @param group the group to write + * @return true if successful. + */ + public static boolean writeMaterialGroup(@NotNull AbstractMaterialGroup group){ + return writeMaterialGroup(group, true); + } + + /** + * Write a material group to the config file. + *

+ * You should use {@link #addMaterialGroup(AbstractMaterialGroup)} or {@link #writeMaterialGroup(AbstractMaterialGroup)} instead + * + * @param group the group to write + * @param updatePlanned if we should plan a global update for material groups + * @return true if successful. + */ + public static boolean writeMaterialGroup(@NotNull AbstractMaterialGroup group, boolean updatePlanned){ + String name = group.getName(); + if(name.contains(".")) { + CustomAnvil.instance.getLogger().warning("Group " + name +" contain . in its name but should not. this material group is ignored."); + return false; + } + + if(group instanceof IncludeGroup includeGroup){ + writeKnownGroup("include", includeGroup); + }else if(group instanceof ExcludeGroup excludeGroup){ + writeKnownGroup("exclude", excludeGroup); + }else{ + writeUnknownGroup(group); + } + + prepareSaveTask(); + if(updatePlanned) prepareUpdateTask(); + + return true; + } + + private static void writeKnownGroup(@NotNull String groupType, @NotNull AbstractMaterialGroup group){ + FileConfiguration config = ConfigHolder.ITEM_GROUP_HOLDER.getConfig(); + + String basePath = group.getName() + "."; + Set materialSet = group.getNonGroupInheritedMaterials(); + Set groupSet = group.getGroups(); + + config.set(basePath + ItemGroupManager.GROUP_TYPE_PATH, groupType); + if(!materialSet.isEmpty()){ + config.set(basePath + ItemGroupManager.MATERIAL_LIST_PATH, materialSetToStringList(materialSet)); + } + if(!groupSet.isEmpty()){ + config.set(basePath + ItemGroupManager.GROUP_LIST_PATH, materialGroupSEtToStringList(groupSet)); + } + + } + + private static void writeUnknownGroup(@NotNull AbstractMaterialGroup group) { + FileConfiguration config = ConfigHolder.ITEM_GROUP_HOLDER.getConfig(); + + String basePath = group.getName() + "."; + EnumSet materials = group.getMaterials(); + + config.set(basePath + ItemGroupManager.GROUP_TYPE_PATH, "include"); + if(!materials.isEmpty()){ + config.set(basePath + ItemGroupManager.MATERIAL_LIST_PATH, materialSetToStringList(materials)); + } + + } + + public static List materialSetToStringList(@NotNull Set materials){ + return materials.stream().map(material -> material.getKey().getKey().toLowerCase()).toList(); + } + + public static List materialGroupSEtToStringList(@NotNull Set groups){ + return groups.stream().map(AbstractMaterialGroup::getName).toList(); + } + + /** + * Remove a material group. + * Caution ! It will not be removed from depending conflict or other material group at runtime. + * For that reason, it is not recommended to use this function. + * + * @param group The recipe to remove + * @return True if successful. + */ + public static boolean removeGroup(@NotNull AbstractMaterialGroup group){ + // Remove from registry + ConfigHolder.ITEM_GROUP_HOLDER.getItemGroupsManager().groupMap.remove(group.getName()); + + // Write as null and save to file + ConfigHolder.ITEM_GROUP_HOLDER.getConfig().set(group.getName(), null); + prepareSaveTask(); + + // Remove from gui + if(group instanceof IncludeGroup includeGroup){ + GroupConfigGui.INSTANCE.removeGeneric(includeGroup); + } + + return true; + } + + /** + * Prepare a task to reload every conflict. + */ + private static void prepareSaveTask() { + if(saveChangeTask != -1) return; + + saveChangeTask = Bukkit.getScheduler().scheduleSyncDelayedTask(CustomAnvil.instance, ()->{ + ConfigHolder.ITEM_GROUP_HOLDER.saveToDisk(true); + saveChangeTask = -1; + }, 0L); + } + + /** + * Prepare a task to save configuration. + */ + private static void prepareUpdateTask() { + if(reloadChangeTask != -1) return; + + reloadChangeTask = Bukkit.getScheduler().scheduleSyncDelayedTask(CustomAnvil.instance, ()->{ + ConfigHolder.ITEM_GROUP_HOLDER.reload(); + GroupConfigGui.INSTANCE.reloadValues(); + reloadChangeTask = -1; + }, 0L); + + } + + /** + * Get by name a group. + * + * @param groupName the group name used to fetch + * @return the abstract group of this name. null if not found. + */ + @Nullable + public static AbstractMaterialGroup getGroup(@NotNull String groupName){ + return ConfigHolder.ITEM_GROUP_HOLDER.getItemGroupsManager().get(groupName); + } + + /** + * Get every registered material groups. + * @return An immutable map of group name as its key and group as mapped value. + */ + @NotNull + public static Map getRegisteredGroups(){ + return Collections.unmodifiableMap(ConfigHolder.ITEM_GROUP_HOLDER.getItemGroupsManager().getGroupMap()); + } + +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/CAConfigReadyEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/CAConfigReadyEvent.java new file mode 100644 index 0000000..24691db --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/CAConfigReadyEvent.java @@ -0,0 +1,19 @@ +package xyz.alexcrea.cuanvil.api.event; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class CAConfigReadyEvent extends Event { + + private static final HandlerList HANDLERS = new HandlerList(); + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/CAEnchantRegistryReadyEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/CAEnchantRegistryReadyEvent.java new file mode 100644 index 0000000..3e2fdf8 --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/CAEnchantRegistryReadyEvent.java @@ -0,0 +1,18 @@ +package xyz.alexcrea.cuanvil.api.event; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class CAEnchantRegistryReadyEvent extends Event { + + private static final HandlerList HANDLERS = new HandlerList(); + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/config/ConfigHolder.java b/src/main/java/xyz/alexcrea/cuanvil/config/ConfigHolder.java index 8999b8b..3cfaf03 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/config/ConfigHolder.java +++ b/src/main/java/xyz/alexcrea/cuanvil/config/ConfigHolder.java @@ -20,21 +20,38 @@ public abstract class ConfigHolder { public static UnitRepairHolder UNIT_REPAIR_HOLDER; public static CustomAnvilCraftHolder CUSTOM_RECIPE_HOLDER; - public static boolean loadConfig() { + /** + * Load default configuration. + * @return True if successful. + */ + public static boolean loadDefaultConfig() { DEFAULT_CONFIG = new DefaultConfigHolder(); + + return DEFAULT_CONFIG.reloadFromDisk(true); + } + + /** + * Load non default configuration. + * @return True if successful. + */ + public static boolean loadNonDefaultConfig() { ITEM_GROUP_HOLDER = new ItemGroupConfigHolder(); CONFLICT_HOLDER = new ConflictConfigHolder(); UNIT_REPAIR_HOLDER = new UnitRepairHolder(); CUSTOM_RECIPE_HOLDER = new CustomAnvilCraftHolder(); - return reloadAllFromDisk(true); + return removeNonDefaultFromDisk(true); } public static boolean reloadAllFromDisk(boolean hardfail) { - boolean sucess = DEFAULT_CONFIG.reloadFromDisk(hardfail); if (!sucess) return false; - sucess = ITEM_GROUP_HOLDER.reloadFromDisk(hardfail); + + return removeNonDefaultFromDisk(hardfail); + } + + private static boolean removeNonDefaultFromDisk(boolean hardfail){ + boolean sucess = ITEM_GROUP_HOLDER.reloadFromDisk(hardfail); if (!sucess) return false; sucess = CONFLICT_HOLDER.reloadFromDisk(hardfail); if (!sucess) return false; @@ -192,7 +209,7 @@ public abstract class ConfigHolder { // Class for itemGroupsManager config public static class ItemGroupConfigHolder extends ResourceConfigHolder { - private final static String FILE_NAME = "item_groups"; + private static final String FILE_NAME = "item_groups"; ItemGroupManager itemGroupsManager; @@ -219,7 +236,7 @@ public abstract class ConfigHolder { // Class for enchant conflict config public static class ConflictConfigHolder extends ResourceConfigHolder { - private final static String FILE_NAME = "enchant_conflict"; + private static final String FILE_NAME = "enchant_conflict"; EnchantConflictManager conflictManager; @@ -243,7 +260,7 @@ public abstract class ConfigHolder { // Class for unit repair config public static class UnitRepairHolder extends ResourceConfigHolder { - private final static String ITEM_GROUP_FILE_NAME = "unit_repair_item"; + private static final String ITEM_GROUP_FILE_NAME = "unit_repair_item"; private UnitRepairHolder() { @@ -259,7 +276,7 @@ public abstract class ConfigHolder { // Class for custom anvil craft public static class CustomAnvilCraftHolder extends ResourceConfigHolder { - private final static String CUSTOM_RECIPE_FILE_NAME = "custom_recipes"; + private static final String CUSTOM_RECIPE_FILE_NAME = "custom_recipes"; CustomAnvilRecipeManager recipeManager; private CustomAnvilCraftHolder() { diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantment.java index 4a9a766..0303733 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantment.java +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantment.java @@ -1,28 +1,25 @@ package xyz.alexcrea.cuanvil.enchant; -import io.delilaheve.util.ItemUtil; -import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.entity.HumanEntity; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import xyz.alexcrea.cuanvil.dependency.DependencyManager; -import xyz.alexcrea.cuanvil.dependency.EnchantmentSquaredDependency; -import xyz.alexcrea.cuanvil.group.ConflictType; +import xyz.alexcrea.cuanvil.enchant.bulk.BulkCleanEnchantOperation; +import xyz.alexcrea.cuanvil.enchant.bulk.BulkGetEnchantOperation; import xyz.alexcrea.cuanvil.group.EnchantConflictGroup; import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.function.Supplier; /** * Represent an enchantment compatible with Custom Anvil. - * One issue with custom anvil is: it does not handle well duplicate key name (ignoring namespace) as the plugin was coded with vanilla enchantment in head + * One issue with custom anvil is: it does not handle well duplicate key name (ignoring namespace) + * as the plugin was initially coded with vanilla enchantment in head */ +@SuppressWarnings("unused") public interface CAEnchantment { @@ -54,10 +51,16 @@ public interface CAEnchantment { int defaultMaxLevel(); /** - * Check if the enchantment have specialised group operation. - * @return If the enchantment is optimised for group operation. + * Check if the enchantment have specialised get bulk operation. + * @return If the enchantment is optimised for get bulk operation. */ - boolean isOptimised(); + boolean isGetOptimised(); + + /** + * Check if the enchantment have specialised clean bulk operation. + * @return If the enchantment is optimised for clean bulk operation. + */ + boolean isCleanOptimised(); /** * Check if the player is allowed to use this enchantment. @@ -91,14 +94,20 @@ public interface CAEnchantment { /** * Get current level of the enchantment. - * @param item Item to search the level for. + * @param item Item to search the level for. Should not get changed. + * @return Current leve of this enchantment on item. or 0 if absent. */ - int getLevel(@NotNull ItemStack item); + default int getLevel(@NotNull ItemStack item){ + ItemMeta meta = item.getItemMeta(); + if(meta == null) return 0; + + return getLevel(item, meta); + } /** * Get current level of the enchantment. - * @param item Item to search the level for. - * @param meta Meta of the provided item. It will not be changed and not be set on the item. + * @param item Item to search the level for. Should not get changed. + * @param meta Meta of the provided item. Should not get changed. * @return Current leve of this enchantment on item. or 0 if absent. */ int getLevel(@NotNull ItemStack item, @NotNull ItemMeta meta); @@ -137,29 +146,23 @@ public interface CAEnchantment { * @param item Item to be cleared from enchantments. */ static void clearEnchants(@NotNull ItemStack item){ + // Optimised enchantment clean using item stack + for (BulkCleanEnchantOperation cleanOperator : CAEnchantmentRegistry.getInstance().getOptimisedCleanOperators()) { + cleanOperator.bulkClear(item); + } + ItemMeta meta = item.getItemMeta(); if(meta == null) return; - // Clean Vanilla enchants - if (ItemUtil.INSTANCE.isEnchantedBook(item)) { - EnchantmentStorageMeta bookMeta = (EnchantmentStorageMeta) meta; - bookMeta.getStoredEnchants().forEach( - (enchantment, leve) -> bookMeta.removeStoredEnchant(enchantment) - ); - } else { - item.getEnchantments().forEach( - (enchantment, leve) -> item.removeEnchantment(enchantment) - ); + // Optimised enchantment clean using item meta + for (BulkCleanEnchantOperation cleanOperator : CAEnchantmentRegistry.getInstance().getOptimisedCleanOperators()) { + cleanOperator.bulkClear(item, meta); } - // Clean Enchant Squared enchants - EnchantmentSquaredDependency enchantmentSquared = DependencyManager.INSTANCE.getEnchantmentSquaredCompatibility(); - if(enchantmentSquared != null){ - enchantmentSquared.clearEnchantments(item); - } + item.setItemMeta(meta); // Clean unoptimised enchants - for (CAEnchantment enchant : CAEnchantmentRegistry.getInstance().unoptimisedValues()) { + for (CAEnchantment enchant : CAEnchantmentRegistry.getInstance().unoptimisedCleanValues()) { if(enchant.isEnchantmentPresent(item)){ enchant.removeFrom(item); } @@ -179,25 +182,13 @@ public interface CAEnchantment { ItemMeta meta = item.getItemMeta(); if(meta == null) return enchantments; - // Vanilla optimised get - if (ItemUtil.INSTANCE.isEnchantedBook(item)) { - ((EnchantmentStorageMeta)meta).getStoredEnchants().forEach( - (enchantment, level) -> enchantments.put(registry.getByKey(enchantment.getKey()), level) - ); - } else { - item.getEnchantments().forEach( - (enchantment, level) -> enchantments.put(registry.getByKey(enchantment.getKey()), level) - ); - } - - // Enchants Squared get - EnchantmentSquaredDependency enchantmentSquared = DependencyManager.INSTANCE.getEnchantmentSquaredCompatibility(); - if(enchantmentSquared != null){ - enchantmentSquared.getEnchantmentsSquared(item, enchantments); + // Optimised enchantment get + for (BulkGetEnchantOperation getOperator : CAEnchantmentRegistry.getInstance().getOptimisedGetOperators()) { + getOperator.bulkGet(enchantments, item, meta); } // Unoptimised enchantment get - findEnchantsFromSelectedList(item, meta, enchantments, registry.unoptimisedValues()); + findEnchantsFromSelectedList(item, meta, enchantments, registry.unoptimisedGetValues()); return enchantments; } @@ -226,6 +217,8 @@ public interface CAEnchantment { /** * Gets an array of all the registered enchantments. + * + * @param key The enchantment key * @return Array of enchantment. */ static @Nullable CAEnchantment getByKey(@NotNull NamespacedKey key){ @@ -234,6 +227,7 @@ public interface CAEnchantment { /** * Gets a list of all the unoptimised enchantments. + * @param name The enchantment name * @return List of enchantment. */ static @Nullable CAEnchantment getByName(@NotNull String name){ diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentBase.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentBase.java index 7ea4666..05718d5 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentBase.java +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentBase.java @@ -1,21 +1,22 @@ package xyz.alexcrea.cuanvil.enchant; -import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.entity.HumanEntity; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import xyz.alexcrea.cuanvil.group.ConflictType; import xyz.alexcrea.cuanvil.group.EnchantConflictGroup; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.function.Supplier; +/** + * Default implementation of an enchantment compatible with Custom Anvil. + * One issue with custom anvil is: it does not handle well duplicate key name (ignoring namespace) + * as the plugin was initially coded with vanilla enchantment in head + */ public abstract class CAEnchantmentBase implements CAEnchantment { @NotNull @@ -71,7 +72,12 @@ public abstract class CAEnchantmentBase implements CAEnchantment { } @Override - public boolean isOptimised(){ + public boolean isGetOptimised(){ + return false; + } + + @Override + public boolean isCleanOptimised(){ return false; } @@ -80,12 +86,6 @@ public abstract class CAEnchantmentBase implements CAEnchantment { return true; } - public int getLevel(@NotNull ItemStack item){ - ItemMeta meta = item.getItemMeta(); - if(meta == null) return 0; - return getLevel(item, meta); - } - public boolean isEnchantmentPresent(@NotNull ItemStack item){ ItemMeta meta = item.getItemMeta(); if(meta == null) return false; diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java index c00c7c2..1e7cd54 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java @@ -5,13 +5,12 @@ import org.bukkit.NamespacedKey; import org.bukkit.enchantments.Enchantment; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import xyz.alexcrea.cuanvil.dependency.DependencyManager; +import xyz.alexcrea.cuanvil.enchant.bulk.BukkitEnchantBulkOperation; +import xyz.alexcrea.cuanvil.enchant.bulk.BulkCleanEnchantOperation; +import xyz.alexcrea.cuanvil.enchant.bulk.BulkGetEnchantOperation; import xyz.alexcrea.cuanvil.enchant.wrapped.CAVanillaEnchantment; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; +import java.util.*; import java.util.logging.Level; public class CAEnchantmentRegistry { @@ -24,29 +23,38 @@ public class CAEnchantmentRegistry { // Register enchantment functions private final HashMap byKeyMap; private final HashMap byNameMap; - private final List unoptimisedValues; + + private final List unoptimisedGetValues; + private final List unoptimisedCleanValues; + + private final List optimisedGetOperators; + private final List optimisedCleanOperators; private CAEnchantmentRegistry() { byKeyMap = new HashMap<>(); byNameMap = new HashMap<>(); - unoptimisedValues = new ArrayList<>(); + + unoptimisedGetValues = new ArrayList<>(); + unoptimisedCleanValues = new ArrayList<>(); + + optimisedGetOperators = new ArrayList<>(); + optimisedCleanOperators = new ArrayList<>(); } /** * This should only be called on main of custom anvil. * If called more than one time, chance of thing being broken will be high. */ - public void registerStartupEnchantments(){ + public void registerBukkit(){ + // Register enchantment for (Enchantment enchantment : Enchantment.values()) { register(new CAVanillaEnchantment(enchantment)); } - if(DependencyManager.INSTANCE.getEnchantmentSquaredCompatibility() != null){ - DependencyManager.INSTANCE.getEnchantmentSquaredCompatibility().registerEnchantments(); - } - if(DependencyManager.INSTANCE.getEcoEnchantCompatibility() != null){ - DependencyManager.INSTANCE.getEcoEnchantCompatibility().registerEnchantments(); - } + // Add bukkit enchantment bulk operation + BukkitEnchantBulkOperation bukkitOperation = new BukkitEnchantBulkOperation(); + optimisedGetOperators.add(bukkitOperation); + optimisedCleanOperators.add(bukkitOperation); } @@ -56,13 +64,14 @@ public class CAEnchantmentRegistry { * No guarantee that the enchantment will be present on the config gui if registered late. * (By late I mean after custom anvil startup.) * @param enchantment The enchantment to be registered. + * @return If the operation was successful. */ - public void register(@NotNull CAEnchantment enchantment){ + public boolean register(@NotNull CAEnchantment enchantment){ if(byKeyMap.containsKey(enchantment.getKey())){ CustomAnvil.instance.getLogger().log(Level.WARNING, "Duplicate registered enchantment. This should NOT happen.", new IllegalStateException(enchantment.getKey()+" enchantment was already registered")); - return; + return false; } if(byNameMap.containsKey(enchantment.getName())){ CustomAnvil.instance.getLogger().log(Level.WARNING, @@ -74,9 +83,14 @@ public class CAEnchantmentRegistry { byKeyMap.put(enchantment.getKey(), enchantment); byNameMap.put(enchantment.getName(), enchantment); - if(!enchantment.isOptimised()){ - unoptimisedValues.add(enchantment); + if(!enchantment.isGetOptimised()){ + unoptimisedGetValues.add(enchantment); } + if(!enchantment.isCleanOptimised()){ + unoptimisedCleanValues.add(enchantment); + } + + return true; } /** @@ -87,13 +101,17 @@ public class CAEnchantmentRegistry { * No guarantee that the enchantment will absent if the config guis if unregistered late. * (By late I mean after custom anvil startup.) * @param enchantment The enchantment to be unregistered. + * @return If the operation was successful. */ - public void unregister(CAEnchantment enchantment){ - if(enchantment == null) return; + + public boolean unregister(@Nullable CAEnchantment enchantment){ + if(enchantment == null) return false; byKeyMap.remove(enchantment.getKey()); byNameMap.remove(enchantment.getName()); - unoptimisedValues.remove(enchantment); + unoptimisedGetValues.remove(enchantment); + unoptimisedCleanValues.remove(enchantment); + return true; } /** @@ -101,7 +119,8 @@ public class CAEnchantmentRegistry { * @param key Key to fetch. * @return Registered enchantment. null if absent. */ - public @Nullable CAEnchantment getByKey(@NotNull NamespacedKey key){ + @Nullable + public CAEnchantment getByKey(@NotNull NamespacedKey key){ return byKeyMap.get(key); } @@ -110,13 +129,14 @@ public class CAEnchantmentRegistry { * @param name Name to fetch. * @return Registered enchantment. null if absent. */ - public @Nullable CAEnchantment getByName(@NotNull String name){ + @Nullable + public CAEnchantment getByName(@NotNull String name){ return byNameMap.get(name); } /** * Gets an array of all the registered enchantments. - * @return Array of enchantment. + * @return Array of enchantments. */ @NotNull public Collection values() { @@ -124,12 +144,45 @@ public class CAEnchantmentRegistry { } /** - * Gets a list of all the unoptimised enchantments. - * @return List of enchantment. + * Gets a map of all the registered enchantments. + * @return Map of enchantments. + */ + public Map registeredEnchantments() { + return byKeyMap; + } + + /** + * Gets a list of all the unoptimised get operation enchantments. + * @return List of unoptimised enchantments. */ @NotNull - public List unoptimisedValues() { - return unoptimisedValues; + public List unoptimisedGetValues() { + return unoptimisedGetValues; + } + + /** + * Gets a list of all the unoptimised clean operation enchantments. + * @return List of unoptimised enchantments. + */ + @NotNull + public List unoptimisedCleanValues() { + return unoptimisedCleanValues; + } + + /** + * Get "clean optimised operation" for get enchantments. + * @return Get mutable "clean enchantments optimised operation" list. + */ + public List getOptimisedCleanOperators() { + return optimisedCleanOperators; + } + + /** + * Get "get optimised operation" for get enchantments. + * @return Get mutable "get enchantments optimised operation" list. + */ + public List getOptimisedGetOperators() { + return optimisedGetOperators; } } diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/EnchantmentRarity.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/EnchantmentRarity.java index 50fdfdf..57ccb72 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/enchant/EnchantmentRarity.java +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/EnchantmentRarity.java @@ -1,31 +1,31 @@ package xyz.alexcrea.cuanvil.enchant; -// because spigot (1.18) do not support enchantment rarity, I need to do it myself... -public enum EnchantmentRarity { +// because spigot (1.18) do not look like to provide access to enchantment rarity I need to do it myself... +public class EnchantmentRarity { - NO_RARITY(0, 0), - COMMON(1), - UNCOMMON(2), - RARE(4), - VERY_RARE(8); + public static final EnchantmentRarity NO_RARITY = new EnchantmentRarity(0, 0); + public static final EnchantmentRarity COMMON = new EnchantmentRarity(1); + public static final EnchantmentRarity UNCOMMON = new EnchantmentRarity(2); + public static final EnchantmentRarity RARE = new EnchantmentRarity(4); + public static final EnchantmentRarity VERY_RARE = new EnchantmentRarity(8); private final int itemValue; private final int bookValue; - EnchantmentRarity(int itemValue, int bookValue) { + public EnchantmentRarity(int itemValue, int bookValue) { this.itemValue = itemValue; this.bookValue = bookValue; } - EnchantmentRarity(int itemValue) { + public EnchantmentRarity(int itemValue) { this(itemValue, Math.max(1, itemValue / 2)); } - public int getBookValue() { + public final int getBookValue() { return bookValue; } - public int getItemValue() { + public final int getItemValue() { return itemValue; } diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java new file mode 100644 index 0000000..d34bd49 --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java @@ -0,0 +1,47 @@ +package xyz.alexcrea.cuanvil.enchant.bulk; + +import io.delilaheve.util.ItemUtil; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import xyz.alexcrea.cuanvil.api.EnchantmentApi; +import xyz.alexcrea.cuanvil.enchant.CAEnchantment; + +import java.util.Map; + +public class BukkitEnchantBulkOperation implements BulkGetEnchantOperation, BulkCleanEnchantOperation { + + @Override + public void bulkGet(@NotNull Map enchantmentList, @NotNull ItemStack item, @NotNull ItemMeta meta) { + if (ItemUtil.INSTANCE.isEnchantedBook(item)) { + ((EnchantmentStorageMeta)meta).getStoredEnchants().forEach((enchantment, level) -> + enchantmentList.put(EnchantmentApi.getByKey(enchantment.getKey()), level) + ); + } else { + item.getEnchantments().forEach((enchantment, level) -> + enchantmentList.put(EnchantmentApi.getByKey(enchantment.getKey()), level) + ); + } + } + + @Override + public void bulkClear(@NotNull ItemStack item) { + if (item.getType() != Material.ENCHANTED_BOOK) { + item.getEnchantments().forEach((enchantment, leve) -> + item.removeEnchantment(enchantment) + ); + } + } + + @Override + public void bulkClear(@NotNull ItemStack item, @NotNull ItemMeta meta) { + if (item.getType() == Material.ENCHANTED_BOOK) { + EnchantmentStorageMeta bookMeta = (EnchantmentStorageMeta) meta; + bookMeta.getStoredEnchants().forEach((enchantment, leve) -> + bookMeta.removeStoredEnchant(enchantment) + ); + } + } +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BulkCleanEnchantOperation.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BulkCleanEnchantOperation.java new file mode 100644 index 0000000..4b1a225 --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BulkCleanEnchantOperation.java @@ -0,0 +1,28 @@ +package xyz.alexcrea.cuanvil.enchant.bulk; + +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; + +/** + * Bulk operation for clean enchantments operations. + */ +public interface BulkCleanEnchantOperation { + + /** + * Bulk clear part of the enchantments from this item. + * The item can be edited freely. If you need the meta it is preferred to use {@link #bulkClear(ItemStack, ItemMeta)} if possible + * @param item The item to clear enchantment from. + */ + void bulkClear(@NotNull ItemStack item); + + /** + * Bulk clear part of the enchantments from this item meta. + * Item should not be edited as meta will be applied later. + * If you need to edit the item and do not need the meta use {@link #bulkClear(ItemStack)} + * @param item The item source of the item meta. should not be edited. + * @param meta The item meta to clear enchantment from. + */ + void bulkClear(@NotNull ItemStack item, @NotNull ItemMeta meta); + +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BulkGetEnchantOperation.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BulkGetEnchantOperation.java new file mode 100644 index 0000000..a985edd --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BulkGetEnchantOperation.java @@ -0,0 +1,23 @@ +package xyz.alexcrea.cuanvil.enchant.bulk; + +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import xyz.alexcrea.cuanvil.enchant.CAEnchantment; + +import java.util.Map; + +/** + * Bulk operation for get enchantments operations. + */ +public interface BulkGetEnchantOperation { + + /** + * Bulk get part of the stored enchantment of this item. + * @param enchantmentList Mutable map of collected enchantment. should b + * @param item The item to get enchantment from. Should not get edited. + * @param meta The item meta to get enchantment from. Should not get edited. + */ + void bulkGet(@NotNull Map enchantmentList, @NotNull ItemStack item, @NotNull ItemMeta meta); + +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/EnchantSquaredBulkOperation.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/EnchantSquaredBulkOperation.java new file mode 100644 index 0000000..59bd6ec --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/EnchantSquaredBulkOperation.java @@ -0,0 +1,37 @@ +package xyz.alexcrea.cuanvil.enchant.bulk; + +import me.athlaeos.enchantssquared.managers.CustomEnchantManager; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import xyz.alexcrea.cuanvil.dependency.DependencyManager; +import xyz.alexcrea.cuanvil.dependency.EnchantmentSquaredDependency; +import xyz.alexcrea.cuanvil.enchant.CAEnchantment; + +import java.util.Collections; +import java.util.Map; + +public class EnchantSquaredBulkOperation implements BulkGetEnchantOperation, BulkCleanEnchantOperation { + + @Override + public void bulkGet(@NotNull Map enchantmentList, @NotNull ItemStack item, @NotNull ItemMeta meta) { + EnchantmentSquaredDependency enchantmentSquared = DependencyManager.INSTANCE.getEnchantmentSquaredCompatibility(); + if(enchantmentSquared != null){ + enchantmentSquared.getEnchantmentsSquared(item, enchantmentList); + } + } + + + @Override + public void bulkClear(@NotNull ItemStack item) { + EnchantmentSquaredDependency enchantmentSquared = DependencyManager.INSTANCE.getEnchantmentSquaredCompatibility(); + if(enchantmentSquared != null){ + CustomEnchantManager.getInstance().setItemEnchants(item, Collections.emptyMap()); + } + } + + @Override + public void bulkClear(@NotNull ItemStack item, @NotNull ItemMeta meta) { + // item meta is not preferred for enchantment squared clear + } +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEcoEnchant.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEcoEnchant.java index 31991f4..f4ae757 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEcoEnchant.java +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEcoEnchant.java @@ -5,13 +5,11 @@ import com.willfp.ecoenchants.target.EnchantmentTarget; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment; import xyz.alexcrea.cuanvil.enchant.CAEnchantment; import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity; -import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment; -import xyz.alexcrea.cuanvil.group.ConflictType; import java.util.Map; -import java.util.function.Supplier; public class CAEcoEnchant extends CAVanillaEnchantment implements AdditionalTestEnchantment { diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEnchantSquaredEnchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEnchantSquaredEnchantment.java index ffc9415..b3f91a9 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEnchantSquaredEnchantment.java +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEnchantSquaredEnchantment.java @@ -27,14 +27,19 @@ public class CAEnchantSquaredEnchantment extends CAEnchantmentBase { } @Override - public boolean isOptimised() { + public boolean isGetOptimised() { + return true; + } + + @Override + public boolean isCleanOptimised() { return true; } @Override public boolean isAllowed(@NotNull HumanEntity human) { - if(human instanceof Player){ - return this.enchant.hasPermission((Player) human); + if(human instanceof Player player){ + return this.enchant.hasPermission(player); } // Not really ideal for maintainability but will probably never be executed. (At least I hope) boolean required = CustomEnchantManager.getInstance().isRequirePermissions(); diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAVanillaEnchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAVanillaEnchantment.java index 9fef4c1..136abc3 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAVanillaEnchantment.java +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAVanillaEnchantment.java @@ -13,6 +13,9 @@ import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity; import java.util.Locale; +/** + * Custom Anvil enchantment implementation for vanilla registered enchantment. + */ public class CAVanillaEnchantment extends CAEnchantmentBase { private final @NotNull Enchantment enchantment; @@ -29,7 +32,12 @@ public class CAVanillaEnchantment extends CAEnchantmentBase { } @Override - public boolean isOptimised() { + public boolean isGetOptimised() { + return true; + } + + @Override + public boolean isCleanOptimised() { return true; } diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/MainConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/MainConfigGui.java index 3b6ae2c..b6c1062 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/MainConfigGui.java +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/MainConfigGui.java @@ -16,7 +16,7 @@ import java.util.Collections; public class MainConfigGui extends ChestGui { - private final static MainConfigGui INSTANCE = new MainConfigGui(); + private static final MainConfigGui INSTANCE = new MainConfigGui(); public static MainConfigGui getInstance() { return INSTANCE; @@ -58,7 +58,7 @@ public class MainConfigGui extends ChestGui { enchantLimitMeta.setLore(Collections.singletonList("\u00A77Click here to open enchantment level limit menu")); enchantLimitItemstack.setItemMeta(enchantLimitMeta); - GuiItem enchantLimitItem = GuiGlobalItems.goToGuiItem(enchantLimitItemstack, EnchantLimitConfigGui.INSTANCE); + GuiItem enchantLimitItem = GuiGlobalItems.goToGuiItem(enchantLimitItemstack, new EnchantLimitConfigGui()); pane.bindItem('2', enchantLimitItem); // enchant cost item @@ -70,7 +70,7 @@ public class MainConfigGui extends ChestGui { enchantCostMeta.setLore(Collections.singletonList("\u00A77Click here to open enchantment costs menu")); enchantCostItemstack.setItemMeta(enchantCostMeta); - GuiItem enchantCostItem = GuiGlobalItems.goToGuiItem(enchantCostItemstack, EnchantCostConfigGui.INSTANCE); + GuiItem enchantCostItem = GuiGlobalItems.goToGuiItem(enchantCostItemstack, new EnchantCostConfigGui()); pane.bindItem('3', enchantCostItem); // Enchantment Conflicts item diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/BasicConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/BasicConfigGui.java index 513a6f9..4d900dc 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/BasicConfigGui.java +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/BasicConfigGui.java @@ -12,6 +12,7 @@ import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import xyz.alexcrea.cuanvil.config.ConfigHolder; import xyz.alexcrea.cuanvil.dependency.protocolib.PacketManager; import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui; @@ -31,8 +32,9 @@ import java.util.Collections; */ public class BasicConfigGui extends ChestGui implements ValueUpdatableGui { - private static BasicConfigGui INSTANCE; + private static BasicConfigGui INSTANCE = null; + @Nullable public static BasicConfigGui getInstance() { return INSTANCE; } @@ -43,7 +45,7 @@ public class BasicConfigGui extends ChestGui implements ValueUpdatableGui { */ public BasicConfigGui(PacketManager packetManager) { super(4, "\u00A78Basic Config", CustomAnvil.instance); - INSTANCE = this; + if(INSTANCE == null) INSTANCE = this; this.packetManager = packetManager; init(); diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantConflictGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantConflictGui.java index 6020df6..c4181df 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantConflictGui.java +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantConflictGui.java @@ -18,7 +18,7 @@ import java.util.Collection; public class EnchantConflictGui extends MappedGuiListConfigGui { - public final static EnchantConflictGui INSTANCE = new EnchantConflictGui(); + public static final EnchantConflictGui INSTANCE = new EnchantConflictGui(); static { INSTANCE.init(); @@ -36,7 +36,7 @@ public class EnchantConflictGui extends MappedGuiListConfigGui extends MappedElementListConfigGui< T, S > { - public MappedGuiListConfigGui(@NotNull String title) { + protected MappedGuiListConfigGui(@NotNull String title) { super(title); } diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/elements/EnchantConflictSubSettingGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/elements/EnchantConflictSubSettingGui.java index 8c17f39..56c4420 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/elements/EnchantConflictSubSettingGui.java +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/elements/EnchantConflictSubSettingGui.java @@ -118,10 +118,7 @@ public class EnchantConflictSubSettingGui extends MappedToListSubSettingGui impl EnchantConflictManager manager = ConfigHolder.CONFLICT_HOLDER.getConflictManager(); // Remove from enchantment - for (CAEnchantment enchantment : this.enchantConflict.getEnchants()) { - enchantment.removeConflict(this.enchantConflict); - } - manager.conflictList.remove(this.enchantConflict); + manager.removeConflict(this.enchantConflict); // Remove from parent this.parent.removeGeneric(this.enchantConflict); diff --git a/src/main/kotlin/io/delilaheve/CustomAnvil.kt b/src/main/kotlin/io/delilaheve/CustomAnvil.kt index 02423a6..6c4facf 100644 --- a/src/main/kotlin/io/delilaheve/CustomAnvil.kt +++ b/src/main/kotlin/io/delilaheve/CustomAnvil.kt @@ -4,6 +4,8 @@ import io.delilaheve.util.ConfigOptions import org.bukkit.Bukkit import org.bukkit.configuration.file.YamlConfiguration import org.bukkit.plugin.java.JavaPlugin +import xyz.alexcrea.cuanvil.api.event.CAConfigReadyEvent +import xyz.alexcrea.cuanvil.api.event.CAEnchantRegistryReadyEvent import xyz.alexcrea.cuanvil.command.EditConfigExecutor import xyz.alexcrea.cuanvil.command.ReloadExecutor import xyz.alexcrea.cuanvil.config.ConfigHolder @@ -80,8 +82,6 @@ class CustomAnvil : JavaPlugin() { override fun onEnable() { instance = this - val pluginManager = Bukkit.getPluginManager(); - // Disable old plugin name if exist val potentialPlugin = Bukkit.getPluginManager().getPlugin("UnsafeEnchantsPlus") if (potentialPlugin != null) { @@ -90,40 +90,53 @@ class CustomAnvil : JavaPlugin() { logger.warning("Please note CustomAnvil is a more recent version of UnsafeEnchantsPlus") } - // Load dependency - DependencyManager.loadDependency() - - // Register enchantments - CAEnchantmentRegistry.getInstance().registerStartupEnchantments() + // Add commands + prepareCommand() // Load chat listener chatListener = ChatEventListener() - pluginManager.registerEvents(chatListener, this) + server.pluginManager.registerEvents(chatListener, this) + + // Load dependency + DependencyManager.loadDependency() + + // Register anvil events + server.pluginManager.registerEvents(AnvilEventListener(DependencyManager.packetManager), this) + + // Load metrics + Metrics(this, bstatsPluginId) + + // Load other thing later. + // It is so other dependent plugins can implement there event listener before we fire them. + Bukkit.getScheduler().scheduleSyncDelayedTask(this, {loadEnchantmentSystem()}, 0L) + } + + private fun loadEnchantmentSystem(){ + // Load default configuration + if (!ConfigHolder.loadDefaultConfig()) return + + // Register enchantments + CAEnchantmentRegistry.getInstance().registerBukkit() + DependencyManager.registerEnchantments() + + val enchantReadyEvent = CAEnchantRegistryReadyEvent() + server.pluginManager.callEvent(enchantReadyEvent) // Load config - val success = ConfigHolder.loadConfig() - if (!success) return + if (!ConfigHolder.loadNonDefaultConfig()) return // temporary: handle 1.21 update Update_1_21.handleUpdate() - // Handle custom enchant config - DependencyManager.handleConfigChanges(this) + val configReadyEvent = CAConfigReadyEvent() + server.pluginManager.callEvent(configReadyEvent) // Load gui constants //TODO maybe something better later MainConfigGui.getInstance().init(DependencyManager.packetManager) GuiSharedConstant.loadConstants() - // Load metrics - Metrics(this, bstatsPluginId) - - // Add commands to reload the plugin - prepareCommand() - - server.pluginManager.registerEvents( - AnvilEventListener(DependencyManager.packetManager), - this - ) + // Register enchantment of compatible plugin and load configuration change. + DependencyManager.handleCompatibilityConfig() } fun reloadResource( diff --git a/src/main/kotlin/io/delilaheve/util/ItemUtil.kt b/src/main/kotlin/io/delilaheve/util/ItemUtil.kt index 3c415f6..a85af39 100644 --- a/src/main/kotlin/io/delilaheve/util/ItemUtil.kt +++ b/src/main/kotlin/io/delilaheve/util/ItemUtil.kt @@ -29,7 +29,6 @@ object ItemUtil { fun ItemStack.setEnchantmentsUnsafe(enchantments: Map) { CAEnchantment.clearEnchants(this) - //TODO maybe faster methode to add vanilla enchantment. maybe move this function to wrapped enchantment enchantments.forEach { (enchantment, level) -> enchantment.addEnchantmentUnsafe(this, level) } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/command/ReloadExecutor.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/command/ReloadExecutor.kt index 151ba6b..ca5a205 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/command/ReloadExecutor.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/command/ReloadExecutor.kt @@ -36,8 +36,8 @@ class ReloadExecutor : CommandExecutor { // Then update all global gui containing value from config BasicConfigGui.getInstance()?.updateGuiValues() - EnchantCostConfigGui.INSTANCE.updateGuiValues() - EnchantLimitConfigGui.INSTANCE.updateGuiValues() + EnchantCostConfigGui.getInstance()?.updateGuiValues() + EnchantLimitConfigGui.getInstance()?.updateGuiValues() EnchantConflictGui.INSTANCE.reloadValues() GroupConfigGui.INSTANCE.reloadValues() diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt index 57e27d7..f3962d5 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt @@ -1,11 +1,9 @@ package xyz.alexcrea.cuanvil.dependency import org.bukkit.Bukkit -import org.bukkit.plugin.Plugin import xyz.alexcrea.cuanvil.dependency.protocolib.NoProtocoLib import xyz.alexcrea.cuanvil.dependency.protocolib.PacketManager import xyz.alexcrea.cuanvil.dependency.protocolib.ProtocoLibWrapper -import java.io.File object DependencyManager { @@ -19,7 +17,7 @@ object DependencyManager { // ProtocolLib dependency packetManager = if(pluginManager.isPluginEnabled("ProtocolLib")) ProtocoLibWrapper() - else NoProtocoLib() + else NoProtocoLib() // Enchantment Squared dependency if(pluginManager.isPluginEnabled("EnchantsSquared")){ @@ -35,11 +33,14 @@ object DependencyManager { } - fun handleConfigChanges(plugin: Plugin) { - val folder = File(plugin.dataFolder, "compatibility") - + fun handleCompatibilityConfig() { enchantmentSquaredCompatibility?.registerPluginConfiguration() - ecoEnchantCompatibility?.registerPluginConfiguration(folder) + + } + + fun registerEnchantments() { + enchantmentSquaredCompatibility?.registerEnchantments() + ecoEnchantCompatibility?.registerEnchantments() } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/EcoEnchantDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/EcoEnchantDependency.kt index e37237e..713a03c 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/EcoEnchantDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/EcoEnchantDependency.kt @@ -2,14 +2,10 @@ package xyz.alexcrea.cuanvil.dependency import com.willfp.ecoenchants.enchant.EcoEnchants import io.delilaheve.CustomAnvil -import org.bukkit.configuration.file.YamlConfiguration import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.plugin.Plugin -import xyz.alexcrea.cuanvil.config.ConfigHolder -import xyz.alexcrea.cuanvil.enchant.CAEnchantment -import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry +import xyz.alexcrea.cuanvil.api.EnchantmentApi import xyz.alexcrea.cuanvil.enchant.wrapped.CAEcoEnchant -import java.io.File class EcoEnchantDependency(private val ecoEnchantPlugin: Plugin) { @@ -22,57 +18,14 @@ class EcoEnchantDependency(private val ecoEnchantPlugin: Plugin) { } fun registerEnchantments() { - val registery = CAEnchantmentRegistry.getInstance() - for (ecoEnchant in EcoEnchants.values()) { - val enchantments: CAEnchantment = CAEcoEnchant(ecoEnchant) - - registery.unregister(registery.getByKey(ecoEnchant.enchantment.key)) // As eco enchants are considered real enchantment, we need to unregister it. - registery.register(enchantments) - } - } - - fun registerPluginConfiguration(folder: File){ - val compatibilityFile = File(folder, "ecoEnchant.yml") - - if(compatibilityFile.exists()){ - folder.mkdirs() - compatibilityFile.createNewFile() - } - - val config = YamlConfiguration.loadConfiguration(compatibilityFile) - val defaultConfig = ConfigHolder.DEFAULT_CONFIG.config - var doSave = false + CustomAnvil.instance.logger.info("Preparing Eco Enchant compatibility...") for (ecoEnchant in EcoEnchants.values()) { - val enchantment = CAEnchantmentRegistry.getInstance().getByKey(ecoEnchant.enchantmentKey) - - if(enchantment == null){ - CustomAnvil.instance.logger.warning("Could not find " + ecoEnchant.enchantmentKey + "testing compatibility.") - continue - } - - // Write enchantment value if needed - val testPath = "default.${enchantment.key.key}" - if(!config.getBoolean(testPath, false)){ - doSave = true - config[testPath] = true - - defaultConfig["enchant_limits.${enchantment.key.key}"] = enchantment.defaultMaxLevel() - - val rarity = enchantment.defaultRarity() - defaultConfig["enchant_values.${enchantment.key.key}.item"] = rarity.itemValue - defaultConfig["enchant_values.${enchantment.key.key}.book"] = rarity.bookValue - - } - } - - if(doSave){ - config.save(compatibilityFile) - ConfigHolder.DEFAULT_CONFIG.saveToDisk(true) - - CustomAnvil.instance.logger.info("Saved default for new eco enchant enchantments.") + EnchantmentApi.unregisterEnchantment(ecoEnchant.enchantment) // As eco enchants is loaded before custom anvil and register enchantment to registry, we need to unregister old "vanilla" enchant. + EnchantmentApi.registerEnchantment(CAEcoEnchant(ecoEnchant)) } + CustomAnvil.instance.logger.info("Eco Enchant should now work as expected !") } } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/EnchantmentSquaredDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/EnchantmentSquaredDependency.kt index 15aa46e..6a913ec 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/EnchantmentSquaredDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/EnchantmentSquaredDependency.kt @@ -3,14 +3,19 @@ package xyz.alexcrea.cuanvil.dependency import io.delilaheve.CustomAnvil import me.athlaeos.enchantssquared.enchantments.CustomEnchant import me.athlaeos.enchantssquared.managers.CustomEnchantManager +import org.bukkit.Material import org.bukkit.NamespacedKey import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.ItemStack import org.bukkit.plugin.Plugin -import xyz.alexcrea.cuanvil.config.ConfigHolder +import xyz.alexcrea.cuanvil.api.ConflictBuilder +import xyz.alexcrea.cuanvil.api.EnchantmentApi +import xyz.alexcrea.cuanvil.api.MaterialGroupApi import xyz.alexcrea.cuanvil.enchant.CAEnchantment import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry +import xyz.alexcrea.cuanvil.enchant.bulk.EnchantSquaredBulkOperation import xyz.alexcrea.cuanvil.enchant.wrapped.CAEnchantSquaredEnchantment +import xyz.alexcrea.cuanvil.group.IncludeGroup import java.util.* class EnchantmentSquaredDependency(private val enchantmentSquaredPlugin: Plugin) { @@ -31,14 +36,18 @@ class EnchantmentSquaredDependency(private val enchantmentSquaredPlugin: Plugin) } fun registerEnchantments(){ + CustomAnvil.instance.logger.info("Preparing Enchantment Squared compatibility...") + + // Register enchantments for (enchant in CustomEnchantManager.getInstance().allEnchants.values) { - CAEnchantmentRegistry.getInstance().register( - CAEnchantSquaredEnchantment( - enchant - ) - ) + EnchantmentApi.registerEnchantment(CAEnchantSquaredEnchantment(enchant)) } + // Register bulk operation + val bulkOpperations = EnchantSquaredBulkOperation() + EnchantmentApi.addBulkGet(bulkOpperations) + EnchantmentApi.addBulkClean(bulkOpperations) + } fun getEnchantmentsSquared(item: ItemStack, enchantments: MutableMap) { @@ -50,10 +59,6 @@ class EnchantmentSquaredDependency(private val enchantmentSquaredPlugin: Plugin) } - fun clearEnchantments(item: ItemStack) { - CustomEnchantManager.getInstance().removeAllEnchants(item) - } - fun getKeyFromEnchant(enchant: CustomEnchant): NamespacedKey{ return NamespacedKey.fromString(enchant.type.lowercase(Locale.getDefault()), this.enchantmentSquaredPlugin)!! } @@ -61,14 +66,8 @@ class EnchantmentSquaredDependency(private val enchantmentSquaredPlugin: Plugin) return CAEnchantment.getByKey(getKeyFromEnchant(enchant))!! } - - private val IS_READY_PATH = "enchantment_square_ready" fun registerPluginConfiguration(){ - val defaultConfig = ConfigHolder.DEFAULT_CONFIG.config - val isReady = defaultConfig.getBoolean(IS_READY_PATH, false) - if(isReady) return - - CustomAnvil.instance.logger.info("Preparing configuration for Enchantment Squared...") + CustomAnvil.instance.logger.info("Preparing Enchantment Squared config...") // Prepare enchantments val esEnchantments = ArrayList() @@ -76,91 +75,62 @@ class EnchantmentSquaredDependency(private val enchantmentSquaredPlugin: Plugin) esEnchantments.add(getWrappedEnchant(enchant) as CAEnchantSquaredEnchantment) } - // Write default level limit and xp cost - for (enchantment in esEnchantments) { - defaultConfig["enchant_limits.${enchantment.key.key}"] = enchantment.defaultMaxLevel() - - val rarity = enchantment.defaultRarity() - defaultConfig["enchant_values.${enchantment.key.key}.item"] = rarity.itemValue - defaultConfig["enchant_values.${enchantment.key.key}.book"] = rarity.bookValue - } - // Write groups and conflicts writeMissingGroups() writeMaterialRestriction(esEnchantments) writeEnchantmentConflicts(esEnchantments) - // Set ready - defaultConfig[IS_READY_PATH] = true - - // Save - ConfigHolder.DEFAULT_CONFIG.saveToDisk(true) - ConfigHolder.ITEM_GROUP_HOLDER.saveToDisk(true) - ConfigHolder.CONFLICT_HOLDER.saveToDisk(true) - - // Reload - ConfigHolder.ITEM_GROUP_HOLDER.reload() - CustomAnvil.instance.logger.info("Enchantment Squared should now work as expected !") } private fun writeMissingGroups(){ // Write group that do not exist on custom anvil. // (Tools group regroup most of the tool items. I did not create a seperated group for theses) - val groupConfig = ConfigHolder.ITEM_GROUP_HOLDER.config - if(!groupConfig.isConfigurationSection("pickaxes")){ - groupConfig["pickaxes.type"] = "include" - groupConfig["pickaxes.items"] = listOf("wooden_pickaxe", "stone_pickaxe", "iron_pickaxe", "diamond_pickaxe", "golden_pickaxe", "netherite_pickaxe") - } + val pickaxes = IncludeGroup("pickaxes") + pickaxes.addAll(Material.WOODEN_PICKAXE, Material.STONE_PICKAXE, Material.IRON_PICKAXE, Material.DIAMOND_PICKAXE, Material.GOLDEN_PICKAXE, Material.NETHERITE_PICKAXE) + MaterialGroupApi.addMaterialGroup(pickaxes) - if(!groupConfig.isConfigurationSection("shovels")){ - groupConfig["shovels.type"] = "include" - groupConfig["shovels.items"] = listOf("wooden_shovel", "stone_shovel", "iron_shovel", "diamond_shovel", "golden_shovel", "netherite_shovel") - } + val shovels = IncludeGroup("shovels") + shovels.addAll(Material.WOODEN_SHOVEL, Material.STONE_SHOVEL, Material.IRON_SHOVEL, Material.DIAMOND_SHOVEL, Material.GOLDEN_SHOVEL, Material.NETHERITE_SHOVEL) + MaterialGroupApi.addMaterialGroup(shovels) - if(!groupConfig.isConfigurationSection("hoes")){ - groupConfig["hoes.type"] = "include" - groupConfig["hoes.items"] = listOf("wooden_hoe", "stone_hoe", "iron_hoe", "diamond_hoe", "golden_hoe", "netherite_hoe") - } + val hoes = IncludeGroup("hoes") + hoes.addAll(Material.WOODEN_HOE, Material.STONE_HOE, Material.IRON_HOE, Material.DIAMOND_HOE, Material.GOLDEN_HOE, Material.NETHERITE_HOE) + MaterialGroupApi.addMaterialGroup(hoes) - if(!groupConfig.isConfigurationSection("shield")){ - groupConfig["shield.type"] = "include" - groupConfig["shield.items"] = listOf("shield") - } + val shield = IncludeGroup("shield") + shield.addToPolicy(Material.SHIELD) + MaterialGroupApi.addMaterialGroup(shield) - if(!groupConfig.isConfigurationSection("elytra")){ - groupConfig["elytra.type"] = "include" - groupConfig["elytra.items"] = listOf("elytra") - } + val elytra = IncludeGroup("elytra") + elytra.addToPolicy(Material.ELYTRA) + MaterialGroupApi.addMaterialGroup(elytra) - if(!groupConfig.isConfigurationSection("trinkets")){ - groupConfig["trinkets.type"] = "include" - groupConfig["trinkets.items"] = listOf("rotten_flesh") - } + val trinkets = IncludeGroup("trinkets") + trinkets.addToPolicy(Material.ROTTEN_FLESH) + MaterialGroupApi.addMaterialGroup(trinkets) } private fun writeMaterialRestriction(esEnchantments: List){ - val conflictConfig = ConfigHolder.CONFLICT_HOLDER.config for (enchantment in esEnchantments) { - val restrictionName = "restriction_${enchantment.key.key}" - if(!conflictConfig.isConfigurationSection(restrictionName)){ - conflictConfig["$restrictionName.enchantments"] = listOf(enchantment.name) + val conflict = ConflictBuilder("restriction_${enchantment.key.key}", CustomAnvil.instance) + conflict.addEnchantment(enchantment) - // Get allowed groups - val listOfAllowed = ArrayList() - listOfAllowed.add("enchanted_book") // enchanted book is allowed in any case. + // enchanted book is allowed in any case. + conflict.addExcludedGroup("enchanted_book") - for (esGroup in enchantment.enchant.compatibleItems) { - val caGroup = esGroupToCAGroup(esGroup) - if(caGroup == null){ - CustomAnvil.instance.logger.info("Could not find equivalent custom anvil group for $esGroup") - continue - } - listOfAllowed.add(caGroup) + // Get allowed groups + for (esGroup in enchantment.enchant.compatibleItems) { + val caGroup = esGroupToCAGroup(esGroup) + if(caGroup == null){ + CustomAnvil.instance.logger.info("Could not find equivalent custom anvil group for $esGroup") + continue } - conflictConfig["$restrictionName.notAffectedGroups"] = listOfAllowed + conflict.addExcludedGroup(caGroup) } + + conflict.registerIfAbsent() } } @@ -181,18 +151,12 @@ class EnchantmentSquaredDependency(private val enchantmentSquaredPlugin: Plugin) } private fun writeConflict(enchantment1: CAEnchantment, enchantment2: CAEnchantment){ - val conflictConfig = ConfigHolder.CONFLICT_HOLDER.config - val conflictPath = "${enchantment1.name}_with_${enchantment2.name}_conflict" + val conflict = ConflictBuilder("${enchantment1.name}_with_${enchantment2.name}_conflict", CustomAnvil.instance) - if(!conflictConfig.isConfigurationSection(conflictPath)){ - conflictConfig["$conflictPath.enchantments"] = listOf(enchantment1.name, enchantment2.name) - - val empty: List = Collections.emptyList() - conflictConfig["$conflictPath.notAffectedGroups"] = empty - - conflictConfig["$conflictPath.maxEnchantmentBeforeConflict"] = 1 - } + conflict.addEnchantment(enchantment1).addEnchantment(enchantment2) + conflict.setMaxBeforeConflict(1) + conflict.registerIfAbsent() } /** diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/group/AbstractMaterialGroup.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/group/AbstractMaterialGroup.kt index 289f058..ec6e7bc 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/group/AbstractMaterialGroup.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/group/AbstractMaterialGroup.kt @@ -25,13 +25,37 @@ abstract class AbstractMaterialGroup(private val name: String) { /** * Push a material to this group to follow this group policy + * @return this instance. */ - abstract fun addToPolicy(mat: Material) + abstract fun addToPolicy(mat: Material): AbstractMaterialGroup + + /** + * Push a list of material to this group to follow this group policy + * @return this instance. + */ + fun addAll(vararg materials: Material): AbstractMaterialGroup { + for (material in materials) { + addToPolicy(material) + } + return this + } /** * Push a group to this group to follow this group policy + * @return this instance. */ - abstract fun addToPolicy(other: AbstractMaterialGroup) + abstract fun addToPolicy(other: AbstractMaterialGroup): AbstractMaterialGroup + + /** + * Push a list of group to this group to follow this group policy + * @return this instance. + */ + fun addAll(vararg otherList: AbstractMaterialGroup): AbstractMaterialGroup { + for (group in otherList) { + addToPolicy(group) + } + return this + } /** * Get the group contained material as a set diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictGroup.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictGroup.kt index be8d085..c58c6bf 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictGroup.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictGroup.kt @@ -5,7 +5,7 @@ import org.bukkit.Material import xyz.alexcrea.cuanvil.enchant.CAEnchantment class EnchantConflictGroup( - private val name: String, + val name: String, private val cantConflict: AbstractMaterialGroup, var minBeforeBlock: Int ) { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictManager.kt index b2d54d4..abebcbc 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictManager.kt @@ -9,7 +9,6 @@ import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment import xyz.alexcrea.cuanvil.enchant.CAEnchantment import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry import java.util.* -import kotlin.collections.ArrayList class EnchantConflictManager { @@ -28,7 +27,7 @@ class EnchantConflictManager { private const val FUTURE_USE_PATH = "useInFuture" // Default name for a joining group - private const val DEFAULT_GROUP_NAME = "joinedGroup" + const val DEFAULT_GROUP_NAME = "joinedGroup" // 1.20.5 compatibility TODO better update system private val SWEEPING_EDGE_ENCHANT = @@ -53,12 +52,21 @@ class EnchantConflictManager { val section = config.getConfigurationSection(key)!! val conflict = createConflict(section, itemManager, key) - addConflictToEnchantments(conflict) - conflictList.add(conflict) + addConflict(conflict) } } + fun addConflict(conflict: EnchantConflictGroup){ + addConflictToEnchantments(conflict) + conflictList.add(conflict) + } + + fun removeConflict(conflict: EnchantConflictGroup){ + removeConflictFromEnchantments(conflict) + conflictList.remove(conflict) + } + // Add the conflict to enchantments private fun addConflictToEnchantments(conflict: EnchantConflictGroup) { conflict.getEnchants().forEach { enchant -> @@ -66,6 +74,13 @@ class EnchantConflictManager { } } + // Remove the conflict from enchantments + private fun removeConflictFromEnchantments(conflict: EnchantConflictGroup) { + conflict.getEnchants().forEach { enchant -> + enchant.removeConflict(conflict) + } + } + // create and read a conflict from a yaml section private fun createConflict( section: ConfigurationSection, @@ -140,7 +155,7 @@ class EnchantConflictManager { ): AbstractMaterialGroup { val group = itemManager.get(groupName) if (group == null) { - CustomAnvil.instance.logger.warning("Group $groupName do not exist but is ask by conflict $conflictName") + CustomAnvil.instance.logger.warning("Material group $groupName do not exist but is ask by conflict $conflictName") return IncludeGroup("error_placeholder") } @@ -175,7 +190,7 @@ class EnchantConflictManager { if(doConflict){ return ConflictType.ENCHANTMENT_CONFLICT } -; + } } @@ -188,14 +203,14 @@ class EnchantConflictManager { } - return result; + return result } private fun createPartialResult(item: ItemStack, enchantments: Map): ItemStack { val newItem = item.clone() CAEnchantment.clearEnchants(newItem) - enchantments.forEach{//TODO maybe bulk add if possible + enchantments.forEach{ enchantment -> enchantment.key.addEnchantmentUnsafe(newItem, enchantment.value) } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/group/ExcludeGroup.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/group/ExcludeGroup.kt index 247cb83..7684c3f 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/group/ExcludeGroup.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/group/ExcludeGroup.kt @@ -20,14 +20,18 @@ class ExcludeGroup(name: String) : AbstractMaterialGroup(name) { return false } - override fun addToPolicy(mat: Material) { + override fun addToPolicy(mat: Material): ExcludeGroup { includedMaterial.remove(mat) groupItems.remove(mat) + + return this } - override fun addToPolicy(other: AbstractMaterialGroup) { + override fun addToPolicy(other: AbstractMaterialGroup): ExcludeGroup { includedGroup.add(other) groupItems.removeAll(other.getMaterials()) + + return this } override fun setGroups(groups: MutableSet) { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/group/IncludeGroup.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/group/IncludeGroup.kt index 968ceb1..848789f 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/group/IncludeGroup.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/group/IncludeGroup.kt @@ -20,14 +20,18 @@ class IncludeGroup(name: String) : AbstractMaterialGroup(name) { return false } - override fun addToPolicy(mat: Material) { + override fun addToPolicy(mat: Material): IncludeGroup { includedMaterial.add(mat) groupItems.add(mat) + + return this } - override fun addToPolicy(other: AbstractMaterialGroup) { + override fun addToPolicy(other: AbstractMaterialGroup): IncludeGroup { includedGroup.add(other) groupItems.addAll(other.getMaterials()) + + return this } override fun setGroups(groups: MutableSet) { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt index c7e3038..114dbe3 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt @@ -7,7 +7,7 @@ import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant class AnvilCustomRecipe( - private val name: String, + val name: String, var exactCount: Boolean, //var exactLeft: Boolean, //var exactRight: Boolean, @@ -74,25 +74,30 @@ class AnvilCustomRecipe( } - fun saveToFile(){ + fun saveToFile(writeFile: Boolean, doBackup: Boolean){ val fileConfig = ConfigHolder.CUSTOM_RECIPE_HOLDER.config - fileConfig.set("$name.$EXACT_COUNT_CONFIG", exactCount) + fileConfig["$name.$EXACT_COUNT_CONFIG"] = exactCount //fileConfig.set("$name.$EXACT_LEFT_CONFIG", exactLeft) //fileConfig.set("$name.$EXACT_RIGHT_CONFIG", exactRight) - fileConfig.set("$name.$XP_COST_CONFIG", xpCostPerCraft) + fileConfig["$name.$XP_COST_CONFIG"] = xpCostPerCraft - fileConfig.set("$name.$LEFT_ITEM_CONFIG", leftItem) - fileConfig.set("$name.$RIGHT_ITEM_CONFIG", rightItem) - fileConfig.set("$name.$RESULT_ITEM_CONFIG", resultItem) + fileConfig["$name.$LEFT_ITEM_CONFIG"] = leftItem + fileConfig["$name.$RIGHT_ITEM_CONFIG"] = rightItem + fileConfig["$name.$RESULT_ITEM_CONFIG"] = resultItem - if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) { - ConfigHolder.CUSTOM_RECIPE_HOLDER.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE) + if (writeFile) { + ConfigHolder.CUSTOM_RECIPE_HOLDER.saveToDisk(doBackup) } } + @Deprecated("Should use saveToFile(Boolean, Boolean) instead") //TODO determine when an where to save/do backup and remove use of variable like TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE + fun saveToFile(){ + saveToFile(GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE, GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE) + } + fun updateFromFile(){ this.exactCount = ConfigHolder.CUSTOM_RECIPE_HOLDER.config.getBoolean( "$name.$EXACT_COUNT_CONFIG",