From a40d2c6530de75044cec3c9b2c6e91abc32f3d37 Mon Sep 17 00:00:00 2001 From: alexcrea <42614139+alexcrea@users.noreply.github.com> Date: Tue, 9 Jul 2024 13:56:19 +0200 Subject: [PATCH] Added custom anvil recipe api & builder. Fixed Conflict not being registered. Build conflict on the builder instance instead of the ConflictAPI class. --- .../cuanvil/api/AnvilRecipeBuilder.java | 205 ++++++++++++++++++ .../xyz/alexcrea/cuanvil/api/ConflictAPI.java | 83 +------ .../alexcrea/cuanvil/api/ConflictBuilder.java | 75 ++++++- .../cuanvil/api/CustomAnvilRecipeApi.java | 82 +++++++ .../EnchantmentSquaredDependency.kt | 2 +- .../cuanvil/recipe/AnvilCustomRecipe.kt | 21 +- 6 files changed, 386 insertions(+), 82 deletions(-) create mode 100644 src/main/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilder.java create mode 100644 src/main/java/xyz/alexcrea/cuanvil/api/CustomAnvilRecipeApi.java 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 index 0444403..bab98b8 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/ConflictAPI.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/ConflictAPI.java @@ -5,10 +5,8 @@ import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; 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.enchant.CAEnchantment; -import xyz.alexcrea.cuanvil.group.*; +import xyz.alexcrea.cuanvil.group.EnchantConflictGroup; import xyz.alexcrea.cuanvil.gui.config.global.EnchantConflictGui; import java.util.Collections; @@ -31,7 +29,7 @@ public class ConflictAPI { * Write and add a conflict. * Will not write the conflict if it already exists. * - * @param builder The conflict builder to base on + * @param builder The conflict builder to be based on * @return True if successful. */ public static boolean addConflict(@NotNull ConflictBuilder builder){ @@ -40,75 +38,17 @@ public class ConflictAPI { if(!writeConflict(builder, false)) return false; - AbstractMaterialGroup materials = extractGroup(builder); - EnchantConflictGroup conflict = new EnchantConflictGroup(builder.getName(), materials, builder.getMaxBeforeConflict()); - appendEnchantments(builder, conflict); + EnchantConflictGroup conflict = builder.build(); + // Register conflict + ConfigHolder.CONFLICT_HOLDER.getConflictManager().getConflictList().add(conflict); + + // Add conflict to gui EnchantConflictGui.INSTANCE.updateValueForGeneric(conflict, true); return true; } - /** - * Append builders stored enchantments into conflict. - * - * @param builder The builder source - * @param conflict The conflict target - */ - protected static void appendEnchantments(@NotNull ConflictBuilder builder, @NotNull EnchantConflictGroup conflict){ - for (String enchantmentName : builder.getEnchantmentNames()){ - if(appendEnchantment(conflict, EnchantmentApi.getByName(enchantmentName))){ - CustomAnvil.instance.getLogger().warning("Could not find enchantment " + enchantmentName + " for conflict " + builder.getName()); - logConflictOrigin(builder); - } - } - for (NamespacedKey enchantmentKey : builder.getEnchantmentKeys()){ - if(!appendEnchantment(conflict, EnchantmentApi.getByKey(enchantmentKey))){ - CustomAnvil.instance.getLogger().warning("Could not find enchantment " + enchantmentKey + " for conflict " + builder.getName()); - logConflictOrigin(builder); - } - } - } - - /** - * 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. - * - * @param builder The builder source - * @return The abstract material group from the builder. - */ - protected static AbstractMaterialGroup extractGroup(@NotNull ConflictBuilder builder){ - ItemGroupManager itemGroupManager = ConfigHolder.ITEM_GROUP_HOLDER.getItemGroupsManager(); - IncludeGroup group = new IncludeGroup(EnchantConflictManager.DEFAULT_GROUP_NAME); - - for (String groupName : builder.getExcludedGroupNames()) { - AbstractMaterialGroup materialGroup = itemGroupManager.get(groupName); - - if(materialGroup == null){ - CustomAnvil.instance.getLogger().warning("Material group " + groupName + " do not exist but is ask by conflict " + builder.getName()); - logConflictOrigin(builder); - continue; - } - - group.addToPolicy(materialGroup); - } - - return group; - } - /** * Write a conflict to the config file and plan an update of conflicts. *

@@ -135,7 +75,7 @@ public class ConflictAPI { String name = builder.getName(); if(name.contains(".")) { - CustomAnvil.instance.getLogger().warning("Conflict " + name +" contain . in its name but should not. this conflict is ignored."); + CustomAnvil.instance.getLogger().warning("Conflict " + name +" contain \".\" in its name but should not. this conflict is ignored."); logConflictOrigin(builder); return false; } @@ -170,8 +110,9 @@ public class ConflictAPI { return result; } + /** - * Prepare a task to reload every conflict. + * Prepare a task to save conflict configuration. */ private static void prepareSaveTask() { if(saveChangeTask != -1) return; @@ -183,7 +124,7 @@ public class ConflictAPI { } /** - * Prepare a task to save configuration. + * Prepare a task to reload every conflict. */ private static void prepareUpdateTask() { if(reloadChangeTask != -1) return; @@ -196,7 +137,7 @@ public class ConflictAPI { } - private static void logConflictOrigin(@NotNull ConflictBuilder builder){ + static void logConflictOrigin(@NotNull ConflictBuilder builder){ CustomAnvil.instance.getLogger().warning("Conflict " + builder.getName() +" came from " + builder.getSourceName() + "."); } diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/ConflictBuilder.java b/src/main/java/xyz/alexcrea/cuanvil/api/ConflictBuilder.java index fa03b8c..2786dd9 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/ConflictBuilder.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/ConflictBuilder.java @@ -1,11 +1,13 @@ 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.AbstractMaterialGroup; +import xyz.alexcrea.cuanvil.group.*; import java.util.HashSet; import java.util.Set; @@ -342,9 +344,20 @@ public class ConflictBuilder { return clone; } + /** + * 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. + * Register this conflict if not yet registered. * Equivalent to {@link ConflictAPI#addConflict(ConflictBuilder)} * @return True if successful. */ @@ -352,4 +365,62 @@ public class ConflictBuilder { 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..01682e6 --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/api/CustomAnvilRecipeApi.java @@ -0,0 +1,82 @@ +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; + +@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; + } + + // Save to file + recipe.saveToFile(false, false); + prepareSaveTask(); + + // Update gui + CustomRecipeConfigGui.INSTANCE.updateValueForGeneric(recipe, true); + + 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/kotlin/xyz/alexcrea/cuanvil/dependency/EnchantmentSquaredDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/EnchantmentSquaredDependency.kt index 2724dd0..c303e34 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/EnchantmentSquaredDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/EnchantmentSquaredDependency.kt @@ -170,7 +170,7 @@ class EnchantmentSquaredDependency(private val enchantmentSquaredPlugin: Plugin) conflict.addEnchantment(enchantment1).addEnchantment(enchantment2) - conflict.setMaxBeforeConflict(1); + conflict.setMaxBeforeConflict(1) conflict.registerIfAbsent() } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt index c7e3038..801a077 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt @@ -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",