diff --git a/build.gradle.kts b/build.gradle.kts index 3cb551e..b0cf5d6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,7 +28,9 @@ dependencies { compileOnly("org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT") // Gui library - implementation("com.github.stefvanschie.inventoryframework:IF:0.10.17") + val stefvanschie_IF = "com.github.stefvanschie.inventoryframework:IF:0.10.17" + implementation(stefvanschie_IF) + testRuntimeOnly(stefvanschie_IF) // EnchantsSquaredRewritten compileOnly(files("libs/EnchantsSquared.jar")) @@ -61,6 +63,10 @@ dependencies { // include kotlin for the offline jar implementation(kotlin("stdlib")) + + // Test dependency + testImplementation("org.mockbukkit.mockbukkit:mockbukkit-v1.21:4.3.1") + testRuntimeOnly("commons-lang:commons-lang:2.6") } allprojects { @@ -80,8 +86,8 @@ allprojects { dependencies { compileOnly(kotlin("stdlib")) - // Currently not used. but it would be useful to test. - testImplementation(platform("org.junit:junit-bom:5.9.1")) + // Test dependency + testImplementation(platform("org.junit:junit-bom:5.11.3")) testImplementation("org.junit.jupiter:junit-jupiter") } @@ -92,7 +98,7 @@ allprojects { // Configure used version of kotlin and java java { disableAutoTargetJvm() - toolchain.languageVersion.set(JavaLanguageVersion.of(20)) + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) } // Set target version diff --git a/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/gui/version/v1_21R1_ExternGuiTester.kt b/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/gui/version/v1_21R1_ExternGuiTester.kt index 7caa61b..eeee7ec 100644 --- a/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/gui/version/v1_21R1_ExternGuiTester.kt +++ b/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/gui/version/v1_21R1_ExternGuiTester.kt @@ -7,10 +7,28 @@ import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester class v1_21R1_ExternGuiTester: ExternGuiTester { override val wesjdAnvilGuiName = "Wrapper1_21_R1" + var tested = false; + var possible = false; + override fun getContainerClass(view: InventoryView): Class? { + // In case we are in a test environment + if(!tested) testClassExist() + if(!possible) return null + if(view !is CraftInventoryView<*, *>) return null val container = view.handle return container.javaClass } + + fun testClassExist(){ + tested = true; + try { + Class.forName("org.bukkit.craftbukkit.inventory.CraftInventoryView") + possible = true + } catch (e: ClassNotFoundException){ + possible = false + } + } + } diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilder.java b/src/main/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilder.java index 74e8118..9d33bfb 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilder.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilder.java @@ -182,7 +182,7 @@ public class AnvilRecipeBuilder { */ @Nullable // null if missing argument public AnvilCustomRecipe build() { - if(leftItem == null || rightItem == null) return null; + if(leftItem == null || resultItem == null) return null; return new AnvilCustomRecipe( this.name, diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/ConflictAPI.java b/src/main/java/xyz/alexcrea/cuanvil/api/ConflictAPI.java index 8047382..fe2715e 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/ConflictAPI.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/ConflictAPI.java @@ -19,7 +19,8 @@ import java.util.List; @SuppressWarnings("unused") public class ConflictAPI { - private ConflictAPI() {} + private ConflictAPI() { + } private static Object saveChangeTask = null; private static Object reloadChangeTask = null; @@ -27,31 +28,32 @@ public class ConflictAPI { /** * Write and add a conflict. * Will not write the conflict if it already exists. + * Will not be successful if the conflict is empty. * * @param builder The conflict builder to be based on * @return True if successful. */ - public static boolean addConflict(@NotNull ConflictBuilder builder){ + public static boolean addConflict(@NotNull ConflictBuilder builder) { return addConflict(builder, false); } /** * Write and add a conflict. * Will not write the conflict if it already exists. + * Will not be successful if the conflict is empty. * - * @param builder The conflict builder to be based on + * @param builder The conflict builder to be based on * @param overrideDeleted If we should write even if the conflict was previously deleted. * @return True if successful. */ - public static boolean addConflict(@NotNull ConflictBuilder builder, boolean overrideDeleted){ + public static boolean addConflict(@NotNull ConflictBuilder builder, boolean overrideDeleted) { FileConfiguration config = ConfigHolder.CONFLICT_HOLDER.getConfig(); // Test if conflict can be added - if(!overrideDeleted && ConfigHolder.CONFLICT_HOLDER.isDeleted(builder.getName())) return false; - if(config.contains(builder.getName())) return false; - - if(!writeConflict(builder, false)) return false; + if (!overrideDeleted && ConfigHolder.CONFLICT_HOLDER.isDeleted(builder.getName())) return false; + if (config.contains(builder.getName())) return false; + if (!writeConflict(builder, false)) return false; EnchantConflictGroup conflict = builder.build(); // Register conflict @@ -59,7 +61,7 @@ public class ConflictAPI { // Add conflict to gui EnchantConflictGui conflictGui = EnchantConflictGui.getCurrentInstance(); - if(conflictGui != null) conflictGui.updateValueForGeneric(conflict, true); + if (conflictGui != null) conflictGui.updateValueForGeneric(conflict, true); return true; } @@ -69,10 +71,10 @@ public class ConflictAPI { *

* 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. + * @param builder the builder + * @return true if was written successfully. */ - public static boolean writeConflict(@NotNull ConflictBuilder builder){ + public static boolean writeConflict(@NotNull ConflictBuilder builder) { return writeConflict(builder, true); } @@ -83,14 +85,14 @@ public class ConflictAPI { * * @param builder The builder * @param updatePlanned If we should plan a global update for conflicts - * @return True if successful. + * @return true if was written successfully. */ - public static boolean writeConflict(@NotNull ConflictBuilder builder, boolean updatePlanned){ + 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."); + if (name.contains(".")) { + CustomAnvil.instance.getLogger().warning("Conflict " + name + " contain \".\" in its name but should not. this conflict is ignored."); logConflictOrigin(builder); return false; } @@ -99,24 +101,27 @@ public class ConflictAPI { 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()); + 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()); + if (!config.isConfigurationSection(name)) return false; prepareSaveTask(); - if(updatePlanned) prepareUpdateTask(); + 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){ + private static List extractEnchantments(@NotNull ConflictBuilder builder) { List result = new ArrayList<>(builder.getEnchantmentNames()); for (NamespacedKey enchantmentKey : builder.getEnchantmentKeys()) { result.add(enchantmentKey.toString()); @@ -131,7 +136,7 @@ public class ConflictAPI { * @param conflict The conflict to remove * @return True if successful. */ - public static boolean removeConflict(@NotNull EnchantConflictGroup conflict){ + public static boolean removeConflict(@NotNull EnchantConflictGroup conflict) { // Remove from registry ConfigHolder.CONFLICT_HOLDER.getConflictManager().removeConflict(conflict); @@ -141,8 +146,7 @@ public class ConflictAPI { // Remove from gui EnchantConflictGui conflictGui = EnchantConflictGui.getCurrentInstance(); - if(conflictGui != null) conflictGui.removeGeneric(conflict); - + if (conflictGui != null) conflictGui.removeGeneric(conflict); return true; } @@ -151,9 +155,9 @@ public class ConflictAPI { * Prepare a task to save conflict configuration. */ private static void prepareSaveTask() { - if(saveChangeTask != null) return; + if (saveChangeTask != null) return; - saveChangeTask = DependencyManager.scheduler.scheduleGlobally(CustomAnvil.instance, ()->{ + saveChangeTask = DependencyManager.scheduler.scheduleGlobally(CustomAnvil.instance, () -> { ConfigHolder.CONFLICT_HOLDER.saveToDisk(true); saveChangeTask = null; }); @@ -163,28 +167,28 @@ public class ConflictAPI { * Prepare a task to reload every conflict. */ private static void prepareUpdateTask() { - if(reloadChangeTask != null) return; + if (reloadChangeTask != null) return; - reloadChangeTask = DependencyManager.scheduler.scheduleGlobally(CustomAnvil.instance, ()->{ + reloadChangeTask = DependencyManager.scheduler.scheduleGlobally(CustomAnvil.instance, () -> { ConfigHolder.CONFLICT_HOLDER.reload(); EnchantConflictGui conflictGui = EnchantConflictGui.getCurrentInstance(); - if(conflictGui != null) conflictGui.reloadValues(); + if (conflictGui != null) conflictGui.reloadValues(); reloadChangeTask = null; }); - } - static void logConflictOrigin(@NotNull ConflictBuilder builder){ + 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(){ + 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 index e3fd2d6..f662140 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/ConflictBuilder.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/ConflictBuilder.java @@ -36,7 +36,7 @@ public class ConflictBuilder { * @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){ + public ConflictBuilder(@NotNull String name, int maxBeforeConflict, @Nullable Plugin source) { this.source = source; this.name = name; @@ -54,7 +54,7 @@ public class ConflictBuilder { * @param name The conflict name * @param source The conflict source */ - public ConflictBuilder(@NotNull String name, @Nullable Plugin source){ + public ConflictBuilder(@NotNull String name, @Nullable Plugin source) { this(name, 0, source); } @@ -63,7 +63,7 @@ public class ConflictBuilder { * * @param name The conflict name */ - public ConflictBuilder(@NotNull String name){ + public ConflictBuilder(@NotNull String name) { this(name, null); } @@ -84,7 +84,7 @@ public class ConflictBuilder { */ @NotNull public String getSourceName() { - if(source == null) return "an unknown source"; + if (source == null) return "an unknown source"; return source.getName(); } @@ -177,7 +177,7 @@ public class ConflictBuilder { * @return This conflict builder instance. */ @NotNull - public ConflictBuilder addEnchantment(@NotNull String enchantmentName){ + public ConflictBuilder addEnchantment(@NotNull String enchantmentName) { enchantmentNames.add(enchantmentName); return this; } @@ -189,7 +189,7 @@ public class ConflictBuilder { * @return This conflict builder instance. */ @NotNull - public ConflictBuilder addEnchantment(@NotNull NamespacedKey enchantmentKey){ + public ConflictBuilder addEnchantment(@NotNull NamespacedKey enchantmentKey) { enchantmentKeys.add(enchantmentKey); return this; } @@ -201,7 +201,7 @@ public class ConflictBuilder { * @return This conflict builder instance. */ @NotNull - public ConflictBuilder addEnchantment(@NotNull CAEnchantment enchantment){ + public ConflictBuilder addEnchantment(@NotNull CAEnchantment enchantment) { addEnchantment(enchantment.getKey()); return this; } @@ -213,7 +213,7 @@ public class ConflictBuilder { * @return This conflict builder instance. */ @NotNull - public ConflictBuilder removeEnchantment(@NotNull String enchantmentName){ + public ConflictBuilder removeEnchantment(@NotNull String enchantmentName) { enchantmentNames.remove(enchantmentName); return this; } @@ -225,7 +225,7 @@ public class ConflictBuilder { * @return This conflict builder instance. */ @NotNull - public ConflictBuilder removeEnchantment(@NotNull NamespacedKey enchantmentKey){ + public ConflictBuilder removeEnchantment(@NotNull NamespacedKey enchantmentKey) { enchantmentKeys.remove(enchantmentKey); return removeEnchantment(enchantmentKey.getKey()); } @@ -237,7 +237,7 @@ public class ConflictBuilder { * @return This conflict builder instance. */ @NotNull - public ConflictBuilder removeEnchantment(@NotNull CAEnchantment enchantment){ + public ConflictBuilder removeEnchantment(@NotNull CAEnchantment enchantment) { return removeEnchantment(enchantment.getKey()); } @@ -256,7 +256,7 @@ public class ConflictBuilder { * @return This conflict builder instance. */ @NotNull - public ConflictBuilder addExcludedGroup(@NotNull String groupName){ + public ConflictBuilder addExcludedGroup(@NotNull String groupName) { excludedGroupNames.add(groupName); return this; } @@ -276,7 +276,7 @@ public class ConflictBuilder { * @return this conflict builder instance. */ @NotNull - public ConflictBuilder addExcludedGroup(@NotNull AbstractMaterialGroup group){ + public ConflictBuilder addExcludedGroup(@NotNull AbstractMaterialGroup group) { return addExcludedGroup(group.getName()); } @@ -295,7 +295,7 @@ public class ConflictBuilder { * @return This conflict builder instance. */ @NotNull - public ConflictBuilder removeExcludedGroup(@NotNull String groupName){ + public ConflictBuilder removeExcludedGroup(@NotNull String groupName) { excludedGroupNames.remove(groupName); return this; } @@ -315,7 +315,7 @@ public class ConflictBuilder { * @return This conflict builder instance. */ @NotNull - public ConflictBuilder removeExcludedGroup(@NotNull AbstractMaterialGroup group){ + public ConflictBuilder removeExcludedGroup(@NotNull AbstractMaterialGroup group) { return removeExcludedGroup(group.getName()); } @@ -328,7 +328,7 @@ public class ConflictBuilder { public ConflictBuilder copy() { ConflictBuilder copy = new ConflictBuilder(this.name, this.source); - setMaxBeforeConflict(this.maxBeforeConflict); + copy.setMaxBeforeConflict(this.maxBeforeConflict); // Set Enchantments for (NamespacedKey key : this.enchantmentKeys) { @@ -345,11 +345,13 @@ public class ConflictBuilder { return copy; } + /** * Build a new Enchant conflict group by this builder. + * * @return An Enchant conflict group with this builder parameters. */ - public EnchantConflictGroup build(){ + public EnchantConflictGroup build() { AbstractMaterialGroup materials = extractGroups(); EnchantConflictGroup conflict = new EnchantConflictGroup(getName(), materials, getMaxBeforeConflict()); appendEnchantments(conflict); @@ -359,10 +361,21 @@ public class ConflictBuilder { /** * Register this conflict if not yet registered. - * Equivalent to {@link ConflictAPI#addConflict(ConflictBuilder)} + * Equivalent to {@link ConflictAPI#addConflict(ConflictBuilder, boolean) ConflictAPI.addConflict(this, true)}} + * * @return True if successful. */ - public boolean registerIfAbsent(){ + public boolean registerIfAbsent() { + return ConflictAPI.addConflict(this, true); + } + + /** + * Register this conflict if not yet registered or deleted. + * Equivalent to {@link ConflictAPI#addConflict(ConflictBuilder) ConflictAPI.addConflict(this)} + * + * @return True if successful. + */ + public boolean registerIfNew() { return ConflictAPI.addConflict(this); } @@ -371,15 +384,15 @@ public class ConflictBuilder { * * @param conflict The conflict target */ - protected void appendEnchantments(@NotNull EnchantConflictGroup conflict){ - for (String enchantmentName : getEnchantmentNames()){ - if(appendEnchantments(conflict, EnchantmentApi.getListByName(enchantmentName)) == 0){ + protected void appendEnchantments(@NotNull EnchantConflictGroup conflict) { + for (String enchantmentName : getEnchantmentNames()) { + if (appendEnchantments(conflict, EnchantmentApi.getListByName(enchantmentName)) == 0) { 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))){ + 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); } @@ -393,8 +406,8 @@ public class ConflictBuilder { * @param enchantment The enchantment * @return True if successful. */ - protected static boolean appendEnchantment(@NotNull EnchantConflictGroup conflict, @Nullable CAEnchantment enchantment){ - if(enchantment == null) + protected static boolean appendEnchantment(@NotNull EnchantConflictGroup conflict, @Nullable CAEnchantment enchantment) { + if (enchantment == null) return false; conflict.addEnchantment(enchantment); return true; @@ -403,14 +416,14 @@ public class ConflictBuilder { /** * Append a list of enchantments. * - * @param conflict The conflict target + * @param conflict The conflict target * @param enchantments List of enchantment to add * @return Number of enchantment added */ - protected static int appendEnchantments(@NotNull EnchantConflictGroup conflict, @NotNull List enchantments){ + protected static int appendEnchantments(@NotNull EnchantConflictGroup conflict, @NotNull List enchantments) { int numberValid = 0; for (CAEnchantment enchantment : enchantments) { - if(appendEnchantment(conflict, enchantment)){ + if (appendEnchantment(conflict, enchantment)) { numberValid++; } } @@ -423,14 +436,14 @@ public class ConflictBuilder { * * @return The abstract material group from the builder. */ - protected AbstractMaterialGroup extractGroups(){ + 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){ + if (materialGroup == null) { CustomAnvil.instance.getLogger().warning("Material group " + groupName + " do not exist but is ask by conflict " + getName()); ConflictAPI.logConflictOrigin(this); continue; diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/CustomAnvilRecipeApi.java b/src/main/java/xyz/alexcrea/cuanvil/api/CustomAnvilRecipeApi.java index 32db73b..8f80aa3 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/CustomAnvilRecipeApi.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/CustomAnvilRecipeApi.java @@ -78,6 +78,7 @@ public class CustomAnvilRecipeApi { return true; } + // TODO remove by name and/or by builder (as name is keept) (and maybe create a get by name) /** * Remove a custom anvil recipe. * @@ -86,7 +87,8 @@ public class CustomAnvilRecipeApi { */ public static boolean removeRecipe(@NotNull AnvilCustomRecipe recipe){ // Remove from registry - ConfigHolder.CUSTOM_RECIPE_HOLDER.getRecipeManager().cleanRemove(recipe); + boolean result = ConfigHolder.CUSTOM_RECIPE_HOLDER.getRecipeManager().cleanRemove(recipe); + if(!result) return false; // Delete and save to file ConfigHolder.CUSTOM_RECIPE_HOLDER.delete(recipe.getName()); diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/MaterialGroupApi.java b/src/main/java/xyz/alexcrea/cuanvil/api/MaterialGroupApi.java index dd34eb6..250f797 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/MaterialGroupApi.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/MaterialGroupApi.java @@ -22,49 +22,51 @@ import java.util.*; @SuppressWarnings("unused") public class MaterialGroupApi { - private MaterialGroupApi(){} + private MaterialGroupApi() { + } private static Object saveChangeTask = null; private static Object reloadChangeTask = null; - /** * Write and add a group. * Will not write the group if it already exists. + * Will not be successful if the group is empty. * * @param group The group to add * @return true if successful. */ - public static boolean addMaterialGroup(@NotNull AbstractMaterialGroup group){ + public static boolean addMaterialGroup(@NotNull AbstractMaterialGroup group) { return addMaterialGroup(group, false); } /** * Write and add a group. * Will not write the group if it already exists. + * Will not be successful if the group is empty. * - * @param group The group to add + * @param group The group to add * @param overrideDeleted If we should write even if the group was previously deleted. * @return true if successful. */ - public static boolean addMaterialGroup(@NotNull AbstractMaterialGroup group, boolean overrideDeleted){ + public static boolean addMaterialGroup(@NotNull AbstractMaterialGroup group, boolean overrideDeleted) { ItemGroupManager itemGroupManager = ConfigHolder.ITEM_GROUP_HOLDER.getItemGroupsManager(); // Test if it exists/existed - if(!overrideDeleted && ConfigHolder.ITEM_GROUP_HOLDER.isDeleted(group.getName())) return false; - if(itemGroupManager.get(group.getName()) != null) return false; + if (!overrideDeleted && ConfigHolder.ITEM_GROUP_HOLDER.isDeleted(group.getName())) return false; + if (itemGroupManager.get(group.getName()) != null) return false; // Add group itemGroupManager.getGroupMap().put(group.getName(), group); - if(!writeMaterialGroup(group, false)) return false; + if (!writeMaterialGroup(group, false)) return false; - if(group instanceof IncludeGroup includeGroup){ + if (group instanceof IncludeGroup includeGroup) { GroupConfigGui configGui = GroupConfigGui.getCurrentInstance(); - if(configGui != null) configGui.updateValueForGeneric(includeGroup, true); + if (configGui != null) configGui.updateValueForGeneric(includeGroup, true); } - if(ConfigOptions.INSTANCE.getVerboseDebugLog()){ + if (ConfigOptions.INSTANCE.getVerboseDebugLog()) { CustomAnvil.instance.getLogger().info("Registered group " + group.getName()); } @@ -77,9 +79,9 @@ public class MaterialGroupApi { * 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. + * @return true if was written successfully. */ - public static boolean writeMaterialGroup(@NotNull AbstractMaterialGroup group){ + public static boolean writeMaterialGroup(@NotNull AbstractMaterialGroup group) { return writeMaterialGroup(group, true); } @@ -90,64 +92,69 @@ public class MaterialGroupApi { * * @param group the group to write * @param updatePlanned if we should plan a global update for material groups - * @return true if successful. + * @return true if was written successfully. */ - public static boolean writeMaterialGroup(@NotNull AbstractMaterialGroup group, boolean updatePlanned){ + 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."); + 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); + boolean changed; + if (group instanceof IncludeGroup includeGroup) { + changed = writeKnownGroup("include", includeGroup); + } else if (group instanceof ExcludeGroup excludeGroup) { + changed = writeKnownGroup("exclude", excludeGroup); + } else { + changed = writeUnknownGroup(group); } + if (!changed) return false; prepareSaveTask(); - if(updatePlanned) prepareUpdateTask(); + if (updatePlanned) prepareUpdateTask(); return true; } - private static void writeKnownGroup(@NotNull String groupType, @NotNull AbstractMaterialGroup group){ + private static boolean 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()){ + if (!materialSet.isEmpty()) { config.set(basePath + ItemGroupManager.MATERIAL_LIST_PATH, materialSetToStringList(materialSet)); } - if(!groupSet.isEmpty()){ - config.set(basePath + ItemGroupManager.GROUP_LIST_PATH, materialGroupSEtToStringList(groupSet)); + if (!groupSet.isEmpty()) { + config.set(basePath + ItemGroupManager.GROUP_LIST_PATH, materialGroupSetToStringList(groupSet)); } + if (!config.isConfigurationSection(group.getName())) return false; + config.set(basePath + ItemGroupManager.GROUP_TYPE_PATH, groupType); + return true; } - private static void writeUnknownGroup(@NotNull AbstractMaterialGroup group) { + private static boolean 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)); - } + if (materials.isEmpty()) return false; + config.set(basePath + ItemGroupManager.GROUP_TYPE_PATH, "include"); + config.set(basePath + ItemGroupManager.MATERIAL_LIST_PATH, materialSetToStringList(materials)); + + return true; } - public static List materialSetToStringList(@NotNull Set 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){ + public static List materialGroupSetToStringList(@NotNull Set groups) { return groups.stream().map(AbstractMaterialGroup::getName).toList(); } @@ -157,20 +164,21 @@ public class MaterialGroupApi { * For that reason, it is not recommended to use this function. * * @param group The recipe to remove - * @return True if successful. + * @return True if the group was present. */ - public static boolean removeGroup(@NotNull AbstractMaterialGroup group){ + public static boolean removeGroup(@NotNull AbstractMaterialGroup group) { // Remove from registry - ConfigHolder.ITEM_GROUP_HOLDER.getItemGroupsManager().groupMap.remove(group.getName()); + AbstractMaterialGroup removed = ConfigHolder.ITEM_GROUP_HOLDER.getItemGroupsManager().groupMap.remove(group.getName()); + if (removed == null) return false; // Delete and save to file ConfigHolder.ITEM_GROUP_HOLDER.delete(group.getName()); prepareSaveTask(); // Remove from gui - if(group instanceof IncludeGroup includeGroup){ + if (group instanceof IncludeGroup includeGroup) { GroupConfigGui configGui = GroupConfigGui.getCurrentInstance(); - if(configGui != null) configGui.removeGeneric(includeGroup); + if (configGui != null) configGui.removeGeneric(includeGroup); } return true; @@ -180,9 +188,9 @@ public class MaterialGroupApi { * Prepare a task to reload every conflict. */ private static void prepareSaveTask() { - if(saveChangeTask != null) return; + if (saveChangeTask != null) return; - saveChangeTask = DependencyManager.scheduler.scheduleGlobally(CustomAnvil.instance, ()->{ + saveChangeTask = DependencyManager.scheduler.scheduleGlobally(CustomAnvil.instance, () -> { ConfigHolder.ITEM_GROUP_HOLDER.saveToDisk(true); saveChangeTask = null; }); @@ -192,13 +200,13 @@ public class MaterialGroupApi { * Prepare a task to save configuration. */ private static void prepareUpdateTask() { - if(reloadChangeTask != null) return; + if (reloadChangeTask != null) return; - reloadChangeTask = DependencyManager.scheduler.scheduleGlobally(CustomAnvil.instance, ()->{ + reloadChangeTask = DependencyManager.scheduler.scheduleGlobally(CustomAnvil.instance, () -> { ConfigHolder.ITEM_GROUP_HOLDER.reload(); GroupConfigGui configGui = GroupConfigGui.getCurrentInstance(); - if(configGui != null) configGui.reloadValues(); + if (configGui != null) configGui.reloadValues(); reloadChangeTask = null; }); @@ -212,16 +220,17 @@ public class MaterialGroupApi { * @return the abstract group of this name. null if not found. */ @Nullable - public static AbstractMaterialGroup getGroup(@NotNull String groupName){ + 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(){ + public static Map getRegisteredGroups() { return Collections.unmodifiableMap(ConfigHolder.ITEM_GROUP_HOLDER.getItemGroupsManager().getGroupMap()); } diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/UnitRepairApi.java b/src/main/java/xyz/alexcrea/cuanvil/api/UnitRepairApi.java index fb68448..bc50c16 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/UnitRepairApi.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/UnitRepairApi.java @@ -117,9 +117,10 @@ public class UnitRepairApi { String repairableName = repairable.name(); FileConfiguration config = ConfigHolder.UNIT_REPAIR_HOLDER.getConfig(); - config.set(unitName.toLowerCase() + repairableName.toUpperCase(), null); - config.set(unitName.toUpperCase() + repairableName.toLowerCase(), null); - config.set(unitName.toUpperCase() + repairableName.toUpperCase(), null); + config.set(unitName.toLowerCase() + "." + repairableName.toUpperCase(), null); + config.set(unitName.toUpperCase() + "." + repairableName.toLowerCase(), null); + config.set(unitName.toUpperCase() + "." + repairableName.toUpperCase(), null); + config.set(unitName.toLowerCase() + "." + repairableName.toLowerCase(), null); // Test if it was the last value of this section boolean lastValue = false; @@ -142,7 +143,7 @@ public class UnitRepairApi { // We only need to "delete" as the lower case to be counted as deleted - ConfigHolder.UNIT_REPAIR_HOLDER.delete(unitName.toLowerCase() + repairableName.toLowerCase()); + ConfigHolder.UNIT_REPAIR_HOLDER.delete(unitName.toLowerCase() + "." + repairableName.toLowerCase()); prepareSaveTask(); // Remove from gui diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java index d34bd49..cb56436 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java @@ -29,7 +29,8 @@ public class BukkitEnchantBulkOperation implements BulkGetEnchantOperation, Bulk @Override public void bulkClear(@NotNull ItemStack item) { if (item.getType() != Material.ENCHANTED_BOOK) { - item.getEnchantments().forEach((enchantment, leve) -> + + item.getEnchantments().forEach((enchantment, level) -> item.removeEnchantment(enchantment) ); } @@ -43,5 +44,6 @@ public class BukkitEnchantBulkOperation implements BulkGetEnchantOperation, Bulk bookMeta.removeStoredEnchant(enchantment) ); } + } } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt index 4aa69db..9082b95 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt @@ -2,6 +2,7 @@ package xyz.alexcrea.cuanvil.dependency import io.delilaheve.CustomAnvil import org.bukkit.Bukkit +import org.bukkit.entity.HumanEntity import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory @@ -91,11 +92,11 @@ object DependencyManager { } - fun tryEventPreAnvilBypass(event: PrepareAnvilEvent): Boolean { + fun tryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean { var bypass = false // Test if disenchantment used special prepare anvil - if(disenchantmentCompatibility?.testPrepareAnvil(event) == true) bypass = true + if(disenchantmentCompatibility?.testPrepareAnvil(event, player) == true) bypass = true // Test excellent enchantments used special prepare anvil if(!bypass && (excellentEnchantsCompatibility?.testPrepareAnvil(event) == true)) bypass = true diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DisenchantmentDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DisenchantmentDependency.kt index 439dbb4..3ce2ca6 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DisenchantmentDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DisenchantmentDependency.kt @@ -5,6 +5,7 @@ import cz.kominekjan.disenchantment.events.DisenchantEvent import cz.kominekjan.disenchantment.events.ShatterClickEvent import cz.kominekjan.disenchantment.events.ShatterEvent import io.delilaheve.CustomAnvil +import org.bukkit.entity.HumanEntity import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory @@ -70,7 +71,7 @@ class DisenchantmentDependency { } - fun testPrepareAnvil(event: PrepareAnvilEvent): Boolean { + fun testPrepareAnvil(event: PrepareAnvilEvent, player: HumanEntity): Boolean { val previousResult = event.result event.result = null @@ -79,14 +80,14 @@ class DisenchantmentDependency { if(event.result != null) { CustomAnvil.log("Detected pre anvil item extract bypass.") - AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, event.inventory.repairCost) + AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost) return true } splitEvent.onDisenchantmentEvent(event) if(event.result != null) { CustomAnvil.log("Detected pre anvil split enchant bypass.") - AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, event.inventory.repairCost) + AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost) return true } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index b1ec4b9..68370d4 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -41,19 +41,20 @@ class PrepareAnvilListener : Listener { */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) fun anvilCombineCheck(event: PrepareAnvilEvent) { + // Should find player + val player: HumanEntity = event.viewers.first() + // Test if the event should bypass custom anvil. - if(DependencyManager.tryEventPreAnvilBypass(event)) return + if(DependencyManager.tryEventPreAnvilBypass(event, player)) return val inventory = event.inventory val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val second = inventory.getItem(ANVIL_INPUT_RIGHT) - // Should find player - val player = event.view.player if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return // Test custom recipe - if(testCustomRecipe(event, inventory, first, second)) return + if(testCustomRecipe(event, inventory, player, first, second)) return // Test rename lonely item if(second == null) { @@ -75,7 +76,9 @@ class PrepareAnvilListener : Listener { } - private fun testCustomRecipe(event: PrepareAnvilEvent, inventory: AnvilInventory, first: ItemStack, second: ItemStack?): Boolean { + private fun testCustomRecipe(event: PrepareAnvilEvent, inventory: AnvilInventory, + player: HumanEntity, + first: ItemStack, second: ItemStack?): Boolean { val recipe = CustomRecipeUtil.getCustomRecipe(first, second) CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}") if(recipe == null) return false @@ -87,7 +90,7 @@ class PrepareAnvilListener : Listener { event.result = resultItem DependencyManager.treatAnvilResult(event, resultItem) - AnvilXpUtil.setAnvilInvXp(inventory, event.view, recipe.xpCostPerCraft * amount, true) + AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, recipe.xpCostPerCraft * amount, true) return true } @@ -109,7 +112,7 @@ class PrepareAnvilListener : Listener { anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem) - AnvilXpUtil.setAnvilInvXp(inventory, event.view, anvilCost) + AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost) } private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory, player: HumanEntity): Int { @@ -177,7 +180,7 @@ class PrepareAnvilListener : Listener { event.result = resultItem DependencyManager.treatAnvilResult(event, resultItem) - AnvilXpUtil.setAnvilInvXp(inventory, event.view, anvilCost) + AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost) } private fun testUnitRepair(event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity, @@ -203,7 +206,7 @@ class PrepareAnvilListener : Listener { event.result = resultItem DependencyManager.treatAnvilResult(event, resultItem) - AnvilXpUtil.setAnvilInvXp(inventory, event.view, anvilCost) + AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost) return true } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/CustomAnvilRecipeManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/CustomAnvilRecipeManager.kt index 8f9dece..f271d90 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/CustomAnvilRecipeManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/CustomAnvilRecipeManager.kt @@ -9,17 +9,17 @@ class CustomAnvilRecipeManager { lateinit var recipeList: ArrayList - lateinit var recipeByMat: LinkedHashMap> + lateinit var recipeByMat: HashMap> fun prepareRecipes(config: FileConfiguration) { recipeList = ArrayList() - recipeByMat = LinkedHashMap() + recipeByMat = HashMap() // read all configs val keys = config.getKeys(false) for (key in keys) { val recipe = AnvilCustomRecipe.getFromConfig(key) - if(recipe == null){ + if (recipe == null) { CustomAnvil.log("Can't load recipe $key") continue } @@ -30,34 +30,34 @@ class CustomAnvilRecipeManager { } - fun cleanAddNew(recipe: AnvilCustomRecipe){ + fun cleanAddNew(recipe: AnvilCustomRecipe) { recipeList.add(recipe) val leftItem = recipe.leftItem - if(leftItem != null){ + if (leftItem != null) { addToMatMap(recipe, leftItem) } } - fun cleanSetLeftItem(recipe: AnvilCustomRecipe, leftItem: ItemStack?){ + fun cleanSetLeftItem(recipe: AnvilCustomRecipe, leftItem: ItemStack?) { // Remove left item mat if exist val oldLeftItem = recipe.leftItem - if(oldLeftItem != null){ + if (oldLeftItem != null) { val oldMat = oldLeftItem.type val test = recipeByMat[oldMat] test!!.remove(recipe) } - if(leftItem != null){ + if (leftItem != null) { addToMatMap(recipe, leftItem) } recipe.leftItem = leftItem } - private fun addToMatMap(recipe: AnvilCustomRecipe, leftItem: ItemStack){ + private fun addToMatMap(recipe: AnvilCustomRecipe, leftItem: ItemStack) { var recipeList = recipeByMat[leftItem.type] - if(recipeList == null){ + if (recipeList == null) { recipeList = ArrayList() recipeByMat[leftItem.type] = recipeList } @@ -65,11 +65,14 @@ class CustomAnvilRecipeManager { } - fun cleanRemove(recipe: AnvilCustomRecipe) { + fun cleanRemove(recipe: AnvilCustomRecipe): Boolean { - recipeList.remove(recipe) - cleanSetLeftItem(recipe, null) + val exist = recipeList.remove(recipe) + if (exist) { + cleanSetLeftItem(recipe, null) + } + return exist; } } \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt index bdbc8bd..74e604c 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt @@ -6,6 +6,7 @@ import io.delilaheve.util.EnchantmentUtil.enchantmentName import io.delilaheve.util.ItemUtil.findEnchantments import io.delilaheve.util.ItemUtil.isEnchantedBook import org.bukkit.GameMode +import org.bukkit.entity.HumanEntity import org.bukkit.entity.Player import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.InventoryView @@ -25,6 +26,7 @@ object AnvilXpUtil { fun setAnvilInvXp( inventory: AnvilInventory, view: InventoryView, + player: HumanEntity, anvilCost: Int, ignoreRules: Boolean = false ) { @@ -38,8 +40,6 @@ object AnvilXpUtil { anvilCost } - val player = view.player - /* Because Minecraft likes to have the final say in the repair cost displayed * we need to wait for the event to end before overriding it, this ensures that * we have the final say in the process. */ diff --git a/src/test/java/io/delilaheve/CustomAnvilTests.java b/src/test/java/io/delilaheve/CustomAnvilTests.java new file mode 100644 index 0000000..45ecb7c --- /dev/null +++ b/src/test/java/io/delilaheve/CustomAnvilTests.java @@ -0,0 +1,18 @@ +package io.delilaheve; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import xyz.alexcrea.cuanvil.tests.DefaultCustomAnvilTest; + +public class CustomAnvilTests extends DefaultCustomAnvilTest { + + @Test + public void simpleInitTest() { + Assertions.assertNotNull(server); + Assertions.assertNotNull(plugin); + + // Test shutdown + plugin.onDisable(); + } + +} diff --git a/src/test/java/io/delilaheve/util/EnchantmentUtilTests.java b/src/test/java/io/delilaheve/util/EnchantmentUtilTests.java new file mode 100644 index 0000000..0be9f08 --- /dev/null +++ b/src/test/java/io/delilaheve/util/EnchantmentUtilTests.java @@ -0,0 +1,184 @@ +package io.delilaheve.util; + +import io.delilaheve.CustomAnvil; +import org.bukkit.Material; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.permissions.PermissionAttachment; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockbukkit.mockbukkit.entity.PlayerMock; +import org.mockbukkit.mockbukkit.inventory.ItemStackMock; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.tests.ConfigResetCustomAnvilTest; +import xyz.alexcrea.cuanvil.util.AnvilFuseTestData; +import xyz.alexcrea.cuanvil.util.AnvilFuseTestUtil; + +import java.util.List; + +public class EnchantmentUtilTests extends ConfigResetCustomAnvilTest { + + private AnvilInventory anvil; + private PlayerMock player; + + @Override + @BeforeEach + public void setUp() { + super.setUp(); + // Mock used player & open anvil + player = server.addPlayer(); + + Inventory anvil = server.createInventory(player, InventoryType.ANVIL); + + this.anvil = (AnvilInventory) anvil; + player.openInventory(anvil); + + ConfigHolder.DEFAULT_CONFIG.getConfig().set("debug_log", true); + ConfigHolder.DEFAULT_CONFIG.getConfig().set("debug_log_verbose", true); + } + + @Test + public void testBypassFuse(){ + // Test permission did not changed (if it do then server owner should be warned) + String permission = CustomAnvil.bypassFusePermission; + Assertions.assertEquals("ca.bypass.fuse", permission, "bypass fuse permission changed. " + + "Caution with that as it will break some server CustomAnvil setup."); + + // Create ingredient item + ItemStack normalStick = new ItemStackMock(Material.STICK); + ItemStack sharpnessBook = AnvilFuseTestUtil.prepareItem( + Material.ENCHANTED_BOOK, + List.of("sharpness"), 1); + + ItemStack sharpnessStick = AnvilFuseTestUtil.prepareItem( + Material.STICK, + List.of("sharpness"), 1); + + // Create result item + ItemStack sharpnessResultStick = AnvilFuseTestUtil.prepareItem( + Material.STICK, 1, + List.of("sharpness"), 1); + ItemStack sharpness2ResultStick = AnvilFuseTestUtil.prepareItem( + Material.STICK, 1, + List.of("sharpness"), 2); + + // Create failing anvil fuse data + AnvilFuseTestData nullResultData = new AnvilFuseTestData( + normalStick, sharpnessBook, + null + ); + AnvilFuseTestData nullResultData2 = new AnvilFuseTestData( + sharpnessStick, sharpnessStick, + null + ); + + // Create successful anvil fuse data + AnvilFuseTestData legalResultData = new AnvilFuseTestData( + normalStick, sharpnessBook, + sharpnessResultStick + // TODO add expected price + ); + AnvilFuseTestData legalResultData2 = new AnvilFuseTestData( + sharpnessStick, sharpnessStick, + sharpness2ResultStick + // TODO add expected price + ); + + // Test with no permission + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData2); + + // Add permission + PermissionAttachment attachment = player.addAttachment(plugin); + attachment.setPermission(permission, true); + + // Test with new permission + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData2); + } + + @Test + public void testLeveLimitFuse(){ + String permission = CustomAnvil.bypassLevelPermission; + Assertions.assertEquals("ca.bypass.level", permission, "level fuse permission changed. " + + "Caution with that as it will break some server CustomAnvil setup."); + + // Create ingredient item + ItemStack sharpness5Sword = AnvilFuseTestUtil.prepareItem( + Material.DIAMOND_SWORD, + List.of("sharpness"), 5); + + ItemStack sharpnessBook = AnvilFuseTestUtil.prepareItem( + Material.ENCHANTED_BOOK, + List.of("sharpness"), 1); + ItemStack sharpness5Book = AnvilFuseTestUtil.prepareItem( + Material.ENCHANTED_BOOK, + List.of("sharpness"), 5); + ItemStack sharpness6Book = AnvilFuseTestUtil.prepareItem( + Material.ENCHANTED_BOOK, + List.of("sharpness"), 6); + + // Create result item + ItemStack sharpness2BookResult = AnvilFuseTestUtil.prepareItem( + Material.ENCHANTED_BOOK, 1, + List.of("sharpness"), 2); + ItemStack sharpness6SwordResult = AnvilFuseTestUtil.prepareItem( + Material.DIAMOND_SWORD, 1, + List.of("sharpness"), 6); + + // Create failing anvil fuse data + AnvilFuseTestData nullResultData = new AnvilFuseTestData( + sharpnessBook, sharpnessBook, + null + ); + AnvilFuseTestData nullResultData2 = new AnvilFuseTestData( + sharpness5Sword, sharpness6Book, + null + ); + AnvilFuseTestData nullResultData3 = new AnvilFuseTestData( + sharpness5Sword, sharpness5Book, + null + ); + + // Create successful anvil fuse data + AnvilFuseTestData legalResultData = new AnvilFuseTestData( + sharpnessBook, sharpnessBook, + sharpness2BookResult + // TODO add expected price + ); + AnvilFuseTestData legalResultData2 = new AnvilFuseTestData( + sharpness5Sword, sharpness6Book, + sharpness6SwordResult + // TODO add expected price + ); + AnvilFuseTestData legalResultData3 = new AnvilFuseTestData( + sharpness5Sword, sharpness5Book, + sharpness6SwordResult + // TODO add expected price + ); + + // Test failing result first + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData2); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData3); + + // Test working sharpness 2 + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData); + + // Set merge limit to 2 & test + ConfigHolder.DEFAULT_CONFIG.getConfig().set("disable-merge-over.minecraft:sharpness", 1); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData); + + // Add permission + PermissionAttachment attachment = player.addAttachment(plugin); + attachment.setPermission(permission, true); + + // Test working sharpness 2 + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData2); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData3); + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/anvil/AnvilFuseTests.java b/src/test/java/xyz/alexcrea/cuanvil/anvil/AnvilFuseTests.java new file mode 100644 index 0000000..4d540ec --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/anvil/AnvilFuseTests.java @@ -0,0 +1,92 @@ +package xyz.alexcrea.cuanvil.anvil; + +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockbukkit.mockbukkit.entity.PlayerMock; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.tests.SharedCustomAnvilTest; +import xyz.alexcrea.cuanvil.util.AnvilFuseTestData; +import xyz.alexcrea.cuanvil.util.AnvilFuseTestUtil; +import xyz.alexcrea.cuanvil.util.CommonItemUtil; + +public class AnvilFuseTests extends SharedCustomAnvilTest { + + private static AnvilInventory anvil; + private static PlayerMock player; + + @BeforeAll + public static void setUp() { + // Mock used player & open anvil + player = server.addPlayer(); + + Inventory anvil = server.createInventory(player, InventoryType.ANVIL); + + AnvilFuseTests.anvil = (AnvilInventory) anvil; + player.openInventory(anvil); + + ConfigHolder.DEFAULT_CONFIG.getConfig().set("debug_log", true); + ConfigHolder.DEFAULT_CONFIG.getConfig().set("debug_log_verbose", true); + } + + @BeforeEach + public void prepareAnvil(){ + anvil.clear(); + } + + @AfterAll + public static void tearDown() { + player = null; + anvil = null; + } + + @Test + public void mergeFuseTest(){ + // Literally just test a sharpness 4 + sharpness 4 + ItemStack sharpness4 = CommonItemUtil.sharpness(4); + ItemStack sharpness5 = CommonItemUtil.sharpness(5); + + AnvilFuseTestData data = new AnvilFuseTestData( + sharpness4, sharpness4, + sharpness5 + // TODO add expected price + ); + + AnvilFuseTestUtil.executeAnvilTest(anvil, player, data); + } + + @Test + public void overFuseTest(){ + // Test sharpness 4 + sharpness 5 + ItemStack sharpness4 = CommonItemUtil.sharpness(4); + ItemStack sharpness5 = CommonItemUtil.sharpness(5); + + AnvilFuseTestData data = new AnvilFuseTestData( + sharpness4, sharpness5, + sharpness5 + // TODO add expected price + ); + + AnvilFuseTestUtil.executeAnvilTest(anvil, player, data); + } + + @Test + public void underFuseTest(){ + // test sharpness 5 + 4. Custom Anvil should not allow it to be as it result as the same item as left item + ItemStack sharpness4 = CommonItemUtil.sharpness(4); + ItemStack sharpness5 = CommonItemUtil.sharpness(5); + + AnvilFuseTestData data = new AnvilFuseTestData( + sharpness5, sharpness4, + null + ); + + AnvilFuseTestUtil.executeAnvilTest(anvil, player, data); + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilderTest.java b/src/test/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilderTest.java new file mode 100644 index 0000000..2b57bda --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilderTest.java @@ -0,0 +1,85 @@ +package xyz.alexcrea.cuanvil.api; + + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import xyz.alexcrea.cuanvil.tests.SharedOnlyMockBukkit; + +import static org.junit.jupiter.api.Assertions.*; + +public class AnvilRecipeBuilderTest extends SharedOnlyMockBukkit { + + private AnvilRecipeBuilder builder; + + @BeforeEach + public void setup() { + builder = new AnvilRecipeBuilder("test"); + } + + @Test + void createBuilder_NoLeftItem(){ + builder.setResultItem(new ItemStack(Material.STICK)); + + assertNull(builder.build()); + } + + @Test + void createBuilder_NoResultItem(){ + builder.setLeftItem(new ItemStack(Material.STICK)); + + assertNull(builder.build()); + } + + @Test + void createBuilder_minimalist(){ + builder.setLeftItem(new ItemStack(Material.STICK)) + .setResultItem(new ItemStack(Material.STICK)); + + assertNotNull(builder.build()); + } + + @Test + void setLeftItem(){ + assertNull(builder.getLeftItem()); + builder.setLeftItem(new ItemStack(Material.STICK)); + assertNotNull(builder.getLeftItem()); + } + + @Test + void setRightItem(){ + assertNull(builder.getRightItem()); + builder.setRightItem(new ItemStack(Material.STICK)); + assertNotNull(builder.getRightItem()); + } + + @Test + void setResultItem(){ + assertNull(builder.getResultItem()); + builder.setResultItem(new ItemStack(Material.STICK)); + assertNotNull(builder.getResultItem()); + } + + @Test + void setXpCostPerCraft(){ + assertEquals(1, builder.getXpCostPerCraft()); + builder.setXpCostPerCraft(2); + assertEquals(2, builder.getXpCostPerCraft()); + } + + @Test + void setExactCount(){ + assertTrue(builder.isExactCount()); + builder.setExactCount(false); + assertFalse(builder.isExactCount()); + } + + @Test + void setName(){ + assertEquals("test", builder.getName()); + builder.setName("other"); + assertEquals("other", builder.getName()); + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/api/ConflictApiTests.java b/src/test/java/xyz/alexcrea/cuanvil/api/ConflictApiTests.java new file mode 100644 index 0000000..4b43624 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/api/ConflictApiTests.java @@ -0,0 +1,161 @@ +package xyz.alexcrea.cuanvil.api; + +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockbukkit.mockbukkit.entity.PlayerMock; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.enchant.CAEnchantment; +import xyz.alexcrea.cuanvil.group.EnchantConflictGroup; +import xyz.alexcrea.cuanvil.tests.ConfigResetCustomAnvilTest; +import xyz.alexcrea.cuanvil.util.AnvilFuseTestData; +import xyz.alexcrea.cuanvil.util.AnvilFuseTestUtil; +import xyz.alexcrea.cuanvil.util.CommonItemUtil; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ConflictApiTests extends ConfigResetCustomAnvilTest { + + private AnvilInventory anvil; + private PlayerMock player; + + @Override + @BeforeEach + public void setUp() { + super.setUp(); + // Mock used player & open anvil + player = server.addPlayer(); + + Inventory anvil = server.createInventory(player, InventoryType.ANVIL); + + this.anvil = (AnvilInventory) anvil; + player.openInventory(anvil); + + ConfigHolder.DEFAULT_CONFIG.getConfig().set("debug_log", true); + ConfigHolder.DEFAULT_CONFIG.getConfig().set("debug_log_verbose", true); + } + + @Test + public void testConflict() { + ItemStack sharpness1 = CommonItemUtil.sharpness(1); + ItemStack arthropods1 = CommonItemUtil.bane_of_arthropods(1); + ItemStack illegalResult = AnvilFuseTestUtil.prepareItem( + Material.DIAMOND_SWORD, + List.of("bane_of_arthropods", "sharpness"), + 1, 1 + ); + + AnvilFuseTestData nullResultData = new AnvilFuseTestData( + sharpness1, arthropods1, + null + ); + + AnvilFuseTestData legalResultData = new AnvilFuseTestData( + sharpness1, arthropods1, + illegalResult, + // TODO add expected price + null + ); + + CAEnchantment sharpness = EnchantmentApi.getByKey(Enchantment.SHARPNESS.getKey()); + Assertions.assertNotNull(sharpness); + + // Testing default conflict (illegal item should not be produced) + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData); + + // Try to find & remove conflict + EnchantConflictGroup conflict = findGroup("sword_enchant_conflict"); + Assertions.assertNotNull(conflict, "Could not find conflict."); + + // Test what happen when we remove the conflict (illegal item should be allowed) + ConflictAPI.removeConflict(conflict); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData); + + // We create and add a new conflict + ConflictBuilder builder = new ConflictBuilder("sword_enchant_conflict"); + builder.addEnchantment("bane_of_arthropods").addEnchantment(sharpness); //TODO maybe add "add by bukkit enchantment" + builder.setMaxBeforeConflict(1); + + // Nothing should change as it is not new: it was previously deleted + Assertions.assertFalse(builder.registerIfNew()); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData); + + // Now the conflict should be registered and conflict should exist + Assertions.assertTrue(builder.registerIfAbsent()); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData); + } + + @Test + void writeGroup_Reload() { + String conflictName = "conflict"; + ConflictBuilder builder = new ConflictBuilder(conflictName); + builder.addEnchantment("bane_of_arthropods"); + + // Group not being set should not exist + assertFalse(doGroupExist(conflictName)); + + // Add group and reload + assertTrue(ConflictAPI.writeConflict(builder)); + assertFalse(doGroupExist(conflictName)); + + // Tick so write get reloaded + server.getScheduler().performOneTick(); + + assertTrue(doGroupExist(conflictName)); + } + + @Test + void writeGroup_Empty() { + String conflictName = "conflict"; + ConflictBuilder builder = new ConflictBuilder(conflictName); + + // Group not being set should not exist + assertFalse(doGroupExist(conflictName)); + + // Add group and reload + assertFalse(ConflictAPI.writeConflict(builder)); + assertFalse(doGroupExist(conflictName)); + + // Tick so write get reloaded + server.getScheduler().performOneTick(); + + assertFalse(doGroupExist(conflictName)); + } + + @Test + void writeGroup_InvalidDot() { + String conflictName = "conflict.conflict"; + ConflictBuilder builder = new ConflictBuilder(conflictName); + + // Try write group + assertFalse(ConflictAPI.writeConflict(builder)); + } + + // Maybe move to ConflictApi class ? + private static boolean doGroupExist(@NotNull String groupName) { + return findGroup(groupName) != null; + } + + // Maybe move to ConflictApi class ? + @Nullable + private static EnchantConflictGroup findGroup(@NotNull String groupName) { + for (EnchantConflictGroup enchantConflictGroup : ConflictAPI.getRegisteredConflict()) { + if (groupName.equalsIgnoreCase(enchantConflictGroup.getName())) { + return enchantConflictGroup; + } + } + return null; + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/api/ConflictBuilderTests.java b/src/test/java/xyz/alexcrea/cuanvil/api/ConflictBuilderTests.java new file mode 100644 index 0000000..670ae4e --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/api/ConflictBuilderTests.java @@ -0,0 +1,115 @@ +package xyz.alexcrea.cuanvil.api; + +import org.bukkit.NamespacedKey; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import xyz.alexcrea.cuanvil.group.IncludeGroup; +import xyz.alexcrea.cuanvil.tests.SharedOnlyMockBukkit; + +import static org.junit.jupiter.api.Assertions.*; + +public class ConflictBuilderTests extends SharedOnlyMockBukkit { + + private ConflictBuilder builder; + + @BeforeEach + public void setup() { + builder = new ConflictBuilder("test"); + } + + @Test + void testDefaults() { + assertNull(builder.getSource()); + assertEquals("an unknown source", builder.getSourceName()); + assertEquals("test", builder.getName()); + + assertTrue(builder.getEnchantmentNames().isEmpty()); + assertTrue(builder.getEnchantmentKeys().isEmpty()); + assertTrue(builder.getExcludedGroupNames().isEmpty()); + + assertEquals(0, builder.getMaxBeforeConflict()); + } + + @Test + void setName() { + assertEquals("test", builder.getName()); + assertEquals(builder, builder.setName("another")); + assertEquals("another", builder.getName()); + } + + @Test + void setMaxBeforeConflict() { + assertEquals(0, builder.getMaxBeforeConflict()); + assertEquals(builder, builder.setMaxBeforeConflict(1)); + assertEquals(1, builder.getMaxBeforeConflict()); + } + + @Test + void enchantmentString() { + assertTrue(builder.getEnchantmentNames().isEmpty()); + assertEquals(builder, builder.addEnchantment("bane_of_arthropods")); + assertEquals(1, builder.getEnchantmentNames().size()); + + assertEquals(builder, builder.removeEnchantment("bane_of_arthropods")); + assertTrue(builder.getEnchantmentNames().isEmpty()); + } + + @Test + void enchantmentKey() { + NamespacedKey key = NamespacedKey.fromString("bane_of_arthropods"); + assertNotNull(key); + + assertTrue(builder.getEnchantmentKeys().isEmpty()); + assertEquals(builder, builder.addEnchantment(key)); + assertEquals(1, builder.getEnchantmentKeys().size()); + + assertEquals(builder, builder.removeEnchantment(key)); + assertTrue(builder.getEnchantmentKeys().isEmpty()); + } + + @Test + void excludedGroup_String() { + assertTrue(builder.getExcludedGroupNames().isEmpty()); + assertEquals(builder, builder.addExcludedGroup("group")); + assertEquals(1, builder.getExcludedGroupNames().size()); + + assertEquals(builder, builder.removeExcludedGroup("group")); + assertTrue(builder.getExcludedGroupNames().isEmpty()); + } + + @Test + void excludedGroup_Group() { + IncludeGroup group = new IncludeGroup("group"); + + assertTrue(builder.getExcludedGroupNames().isEmpty()); + assertEquals(builder, builder.addExcludedGroup(group)); + assertEquals(1, builder.getExcludedGroupNames().size()); + + assertEquals(builder, builder.removeExcludedGroup(group)); + assertTrue(builder.getExcludedGroupNames().isEmpty()); + } + + @Test + void copy(){ + builder.setName("other"); + builder.setMaxBeforeConflict(1); + + builder.addEnchantment("bane_of_arthropods"); + builder.addEnchantment(NamespacedKey.fromString("bane_of_arthropods")); + builder.addExcludedGroup("group"); + builder.addExcludedGroup(new IncludeGroup("group2")); + + ConflictBuilder copy = builder.copy(); + assertEquals("other", copy.getName()); + assertEquals(1, copy.getMaxBeforeConflict()); + + assertEquals(1, copy.getEnchantmentNames().size()); + assertEquals("bane_of_arthropods", copy.getEnchantmentNames().stream().findFirst().orElse(null)); + + assertEquals(1, copy.getEnchantmentKeys().size()); + assertEquals(NamespacedKey.fromString("bane_of_arthropods"), copy.getEnchantmentKeys().stream().findFirst().orElse(null)); + + assertEquals(2, copy.getExcludedGroupNames().size()); + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/api/CustomAnvilRecipeApiTests.java b/src/test/java/xyz/alexcrea/cuanvil/api/CustomAnvilRecipeApiTests.java new file mode 100644 index 0000000..5b53f0e --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/api/CustomAnvilRecipeApiTests.java @@ -0,0 +1,151 @@ +package xyz.alexcrea.cuanvil.api; + +import org.bukkit.Material; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockbukkit.mockbukkit.entity.PlayerMock; +import org.mockbukkit.mockbukkit.inventory.ItemStackMock; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe; +import xyz.alexcrea.cuanvil.tests.ConfigResetCustomAnvilTest; +import xyz.alexcrea.cuanvil.util.AnvilFuseTestData; +import xyz.alexcrea.cuanvil.util.AnvilFuseTestUtil; + +import static org.junit.jupiter.api.Assertions.*; + +public class CustomAnvilRecipeApiTests extends ConfigResetCustomAnvilTest { + + private AnvilInventory anvil; + private PlayerMock player; + + @Override + @BeforeEach + public void setUp() { + super.setUp(); + // Mock used player & open anvil + player = server.addPlayer(); + + Inventory anvil = server.createInventory(player, InventoryType.ANVIL); + + this.anvil = (AnvilInventory) anvil; + player.openInventory(anvil); + + ConfigHolder.DEFAULT_CONFIG.getConfig().set("debug_log", true); + ConfigHolder.DEFAULT_CONFIG.getConfig().set("debug_log_verbose", true); + } + + @Test + public void testBasicRecipe() { + String recipeName = "stick_recipe"; + ItemStack stick = new ItemStackMock(Material.STICK); + + AnvilFuseTestData nullResultData = new AnvilFuseTestData( + stick, stick, + null + ); + + AnvilFuseTestData legalResultData = new AnvilFuseTestData( + stick, stick, + null, stick, null, + // TODO add expected price + null, + null, null + ); + + // Testing default conflict (no recipe exist) + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData); + + // Add and test recipe + AnvilRecipeBuilder builder = new AnvilRecipeBuilder(recipeName); + builder.setExactCount(true).setLeftItem(stick).setResultItem(stick).setXpCostPerCraft(2); + + assertTrue(builder.registerIfAbsent()); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData); + + AnvilCustomRecipe recipe = getByName(recipeName); + assertNotNull(recipe); + + // Remove recipe + assertTrue(CustomAnvilRecipeApi.removeRecipe(recipe)); + assertFalse(CustomAnvilRecipeApi.removeRecipe(recipe)); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData); + + recipe = getByName(recipeName); + assertNull(recipe); + + // Try to add deleted recipe with no override (should not add) + assertFalse(CustomAnvilRecipeApi.addRecipe(builder, false)); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData); + + recipe = getByName(recipeName); + assertNull(recipe); + + // Try to add deleted recipe with override (should add) + assertTrue(CustomAnvilRecipeApi.addRecipe(builder, true)); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData); + + recipe = getByName(recipeName); + assertNotNull(recipe); + } + + @Test + public void testUnitRecipe() { + String recipeName = "stick_recipe"; + ItemStack stick = new ItemStackMock(Material.STICK); + ItemStack stick2 = new ItemStackMock(Material.STICK, 2); + ItemStack stick5 = new ItemStackMock(Material.STICK, 5); + ItemStack stick10 = new ItemStackMock(Material.STICK, 10); + + AnvilFuseTestData nullResultData = new AnvilFuseTestData( + stick, stick, + null + ); + + AnvilFuseTestData legalResultData1 = new AnvilFuseTestData( + stick, stick, + null, stick2, null, + // TODO add expected price + null, + null, null + ); + + AnvilFuseTestData legalResultData2 = new AnvilFuseTestData( + stick5, stick, + null, stick10, null, + // TODO add expected price + null, + null, null + ); + + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData); + + AnvilRecipeBuilder builder = new AnvilRecipeBuilder(recipeName); + builder.setExactCount(false) + .setLeftItem(stick) + .setResultItem(stick2) + .setXpCostPerCraft(2); + + assertTrue(builder.registerIfAbsent()); + + // Now working test + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData1); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData2); + } + + @Nullable + public static AnvilCustomRecipe getByName(String name){ + for (AnvilCustomRecipe registeredRecipe : CustomAnvilRecipeApi.getRegisteredRecipes()) { + if(registeredRecipe.getName().contentEquals(name)){ + return registeredRecipe; + } + } + + return null; + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/api/MaterialGroupApiTests.java b/src/test/java/xyz/alexcrea/cuanvil/api/MaterialGroupApiTests.java new file mode 100644 index 0000000..164bc58 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/api/MaterialGroupApiTests.java @@ -0,0 +1,108 @@ +package xyz.alexcrea.cuanvil.api; + +import org.bukkit.Material; +import org.junit.jupiter.api.Test; +import xyz.alexcrea.cuanvil.group.EnchantConflictGroup; +import xyz.alexcrea.cuanvil.group.IncludeGroup; +import xyz.alexcrea.cuanvil.tests.ConfigResetCustomAnvilTest; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MaterialGroupApiTests extends ConfigResetCustomAnvilTest { + + @Test + void groupAddAndRemove() { + String groupName = "group"; + IncludeGroup group = new IncludeGroup(groupName); + group.addToPolicy(Material.DIAMOND_PICKAXE); // We do not want it to be empty + + // Group not being set should not exist + assertFalse(doGroupExist(groupName)); + assertFalse(doGroupCanBeFound(groupName)); + + // Add group + assertTrue(MaterialGroupApi.addMaterialGroup(group)); + assertFalse(MaterialGroupApi.addMaterialGroup(group, true)); + + assertTrue(doGroupExist(groupName)); + assertTrue(doGroupCanBeFound(groupName)); + + // Remove group + assertTrue(MaterialGroupApi.removeGroup(group)); + assertFalse(MaterialGroupApi.removeGroup(group)); + + assertFalse(doGroupExist(groupName)); + assertFalse(doGroupCanBeFound(groupName)); + + // Re add + assertFalse(MaterialGroupApi.addMaterialGroup(group, false)); + assertTrue(MaterialGroupApi.addMaterialGroup(group, true)); + + assertTrue(doGroupExist(groupName)); + assertTrue(doGroupCanBeFound(groupName)); + + } + + @Test + void writeGroup_Reload() { + String groupName = "group"; + IncludeGroup group = new IncludeGroup(groupName); + group.addToPolicy(Material.DIAMOND_PICKAXE); // We do not want it to be empty + + // Group not being set should not exist + assertFalse(doGroupExist(groupName)); + assertFalse(doGroupCanBeFound(groupName)); + + // Add group and reload + assertTrue(MaterialGroupApi.writeMaterialGroup(group)); + assertFalse(doGroupExist(groupName)); + assertFalse(doGroupCanBeFound(groupName)); + + // Tick so write get reloaded + server.getScheduler().performOneTick(); + + assertTrue(doGroupExist(groupName)); + assertTrue(doGroupCanBeFound(groupName)); + } + + @Test + void writeGroup_Empty() { + String groupName = "group"; + IncludeGroup group = new IncludeGroup(groupName); + + // Add group and reload + assertFalse(MaterialGroupApi.writeMaterialGroup(group)); + assertFalse(doGroupExist(groupName)); + assertFalse(doGroupCanBeFound(groupName)); + + // Tick so write get reloaded + server.getScheduler().performOneTick(); + + assertFalse(doGroupExist(groupName)); + assertFalse(doGroupCanBeFound(groupName)); + } + + @Test + void writeGroup_InvalidDot() { + String groupName = "group.group"; + IncludeGroup group = new IncludeGroup(groupName); + + // Try write group + assertFalse(MaterialGroupApi.writeMaterialGroup(group)); + } + + boolean doGroupExist(String groupName) { + return MaterialGroupApi.getGroup(groupName) != null; + } + + boolean doGroupCanBeFound(String groupName) { + ConflictBuilder builder = new ConflictBuilder(groupName); + builder.addExcludedGroup(groupName); + + EnchantConflictGroup group = builder.build(); + IncludeGroup materialGroup = (IncludeGroup) group.getCantConflictGroup(); + return materialGroup.getGroups().size() == 1; + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/api/UnitRepairApiTests.java b/src/test/java/xyz/alexcrea/cuanvil/api/UnitRepairApiTests.java new file mode 100644 index 0000000..730aed8 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/api/UnitRepairApiTests.java @@ -0,0 +1,118 @@ +package xyz.alexcrea.cuanvil.api; + +import org.bukkit.Material; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.Repairable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockbukkit.mockbukkit.entity.PlayerMock; +import org.mockbukkit.mockbukkit.inventory.ItemStackMock; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.tests.ConfigResetCustomAnvilTest; +import xyz.alexcrea.cuanvil.util.AnvilFuseTestData; +import xyz.alexcrea.cuanvil.util.AnvilFuseTestUtil; + +import static org.junit.jupiter.api.Assertions.*; + +public class UnitRepairApiTests extends ConfigResetCustomAnvilTest { + + private AnvilInventory anvil; + private PlayerMock player; + + @Override + @BeforeEach + public void setUp() { + super.setUp(); + // Mock used player & open anvil + player = server.addPlayer(); + + Inventory anvil = server.createInventory(player, InventoryType.ANVIL); + + this.anvil = (AnvilInventory) anvil; + player.openInventory(anvil); + + ConfigHolder.DEFAULT_CONFIG.getConfig().set("debug_log", true); + ConfigHolder.DEFAULT_CONFIG.getConfig().set("debug_log_verbose", true); + } + + @Test + void vanillaUnitRepair(){ + ItemStack damagedPickaxe = new ItemStackMock(Material.DIAMOND_PICKAXE); + damagedPickaxe.setDurability((short) (Material.DIAMOND_PICKAXE.getMaxDurability() -1)); + + ItemStack resultPickaxe = new ItemStackMock(Material.DIAMOND_PICKAXE); + resultPickaxe.setDurability((short) (Material.DIAMOND_PICKAXE.getMaxDurability()/2)); + ItemMeta meta = resultPickaxe.getItemMeta(); + ((Repairable) meta).setRepairCost(1); + + ItemStack diamond2 = new ItemStackMock(Material.DIAMOND, 2); + + AnvilFuseTestData legalResultData = new AnvilFuseTestData( + damagedPickaxe, diamond2, + resultPickaxe, + // TODO add expected price + null + ); + + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData); + } + + @Test + void removeUnitRepair(){ + ItemStack damagedPickaxe = new ItemStackMock(Material.DIAMOND_PICKAXE); + damagedPickaxe.setDurability((short) (Material.DIAMOND_PICKAXE.getMaxDurability() -1)); + + ItemStack diamond2 = new ItemStackMock(Material.DIAMOND, 2); + + AnvilFuseTestData nullResultData = new AnvilFuseTestData( + damagedPickaxe, diamond2, + null + ); + + // Remove unit repair + assertTrue(UnitRepairApi.removeUnitRepair(Material.DIAMOND, Material.DIAMOND_PICKAXE)); + + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData); + + // see override + assertFalse(UnitRepairApi.addUnitRepair(Material.DIAMOND, Material.DIAMOND_PICKAXE, 0.25)); + assertTrue(UnitRepairApi.addUnitRepair(Material.DIAMOND, Material.DIAMOND_PICKAXE, 0.25, true)); + } + + + @Test + void addUnitRepair(){ + ItemStack damagedPickaxe = new ItemStackMock(Material.DIAMOND_PICKAXE); + damagedPickaxe.setDurability((short) (Material.DIAMOND_PICKAXE.getMaxDurability() -1)); + + ItemStack resultPickaxe = new ItemStackMock(Material.DIAMOND_PICKAXE); + resultPickaxe.setDurability((short) (Material.DIAMOND_PICKAXE.getMaxDurability()/2)); + ItemMeta meta = resultPickaxe.getItemMeta(); + ((Repairable) meta).setRepairCost(1); + + ItemStack stick2 = new ItemStackMock(Material.STICK, 2); + + AnvilFuseTestData nullResultData = new AnvilFuseTestData( + damagedPickaxe, stick2, + null + ); + AnvilFuseTestData legalResultData = new AnvilFuseTestData( + damagedPickaxe, stick2, + resultPickaxe, + // TODO add expected price + null + ); + + AnvilFuseTestUtil.executeAnvilTest(anvil, player, nullResultData); + + // Add unit repair + assertTrue(UnitRepairApi.addUnitRepair(Material.STICK, Material.DIAMOND_PICKAXE)); + assertFalse(UnitRepairApi.addUnitRepair(Material.STICK, Material.DIAMOND_PICKAXE)); + AnvilFuseTestUtil.executeAnvilTest(anvil, player, legalResultData); + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/config/DefaultConfigTests.java b/src/test/java/xyz/alexcrea/cuanvil/config/DefaultConfigTests.java new file mode 100644 index 0000000..ac3f73e --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/config/DefaultConfigTests.java @@ -0,0 +1,89 @@ +package xyz.alexcrea.cuanvil.config; + +import io.delilaheve.util.ConfigOptions; +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.RegistryKey; +import org.bukkit.NamespacedKey; +import org.bukkit.configuration.file.FileConfiguration; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import xyz.alexcrea.cuanvil.enchant.CAEnchantment; +import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry; +import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity; +import xyz.alexcrea.cuanvil.tests.SharedCustomAnvilTest; + +import java.util.stream.Stream; + +public class DefaultConfigTests extends SharedCustomAnvilTest { + + @ParameterizedTest + @MethodSource("provideStringsForIsConfiguredValueValid") + public void isConfiguredValueValid(String path, Object value){ + FileConfiguration config = ConfigHolder.DEFAULT_CONFIG.getConfig(); + + Assertions.assertEquals(value.toString(), config.getString(path), "Default value is not the same as in the config file"); + } + + @ParameterizedTest + @MethodSource("provideForEnchantmentsTests") + public void testDefaultEnchantLimit(NamespacedKey key){ + CAEnchantment enchantment = CAEnchantmentRegistry.getInstance().getByKey(key); + Assertions.assertNotNull(enchantment, "Enchantment was somehow not found"); + + int levelLimit = ConfigOptions.INSTANCE.enchantLimit(enchantment); + int mergeLimit = ConfigOptions.INSTANCE.maxBeforeMergeDisabled(enchantment); + + Assertions.assertEquals(enchantment.defaultMaxLevel(), levelLimit,"Default enchantment limit is not the same as expected limit "); + + if(mergeLimit >= 0) { // negative mean default + Assertions.assertEquals(enchantment.defaultMaxLevel(), mergeLimit,"Default enchantment merge limit is not the same as expected limit "); + } + + } + + @ParameterizedTest + @MethodSource("provideForEnchantmentsTests") + public void testDefaultEnchantValues(NamespacedKey key){ + CAEnchantment enchantment = CAEnchantmentRegistry.getInstance().getByKey(key); + Assertions.assertNotNull(enchantment, "Enchantment was somehow not found"); + + int itemValue = ConfigOptions.INSTANCE.enchantmentValue(enchantment, false); + int bookValue = ConfigOptions.INSTANCE.enchantmentValue(enchantment, true); + + EnchantmentRarity rarity = enchantment.defaultRarity(); + + Assertions.assertEquals(rarity.getItemValue(), itemValue,"Default enchantment item value is not the same as expected value"); + Assertions.assertEquals(rarity.getBookValue(), bookValue,"Default enchantment book value is not the same as expected value"); + } + + + private static Stream provideStringsForIsConfiguredValueValid() { + return Stream.of( + // Default options + Arguments.of(ConfigOptions.CAP_ANVIL_COST, ConfigOptions.DEFAULT_CAP_ANVIL_COST), + Arguments.of(ConfigOptions.CAP_ANVIL_COST, ConfigOptions.DEFAULT_CAP_ANVIL_COST), + Arguments.of(ConfigOptions.MAX_ANVIL_COST, ConfigOptions.DEFAULT_MAX_ANVIL_COST), + Arguments.of(ConfigOptions.REMOVE_ANVIL_COST_LIMIT, ConfigOptions.DEFAULT_REMOVE_ANVIL_COST_LIMIT), + Arguments.of(ConfigOptions.REPLACE_TOO_EXPENSIVE, ConfigOptions.DEFAULT_REPLACE_TOO_EXPENSIVE), + Arguments.of(ConfigOptions.ITEM_REPAIR_COST, ConfigOptions.DEFAULT_ITEM_REPAIR_COST), + Arguments.of(ConfigOptions.UNIT_REPAIR_COST, ConfigOptions.DEFAULT_UNIT_REPAIR_COST), + Arguments.of(ConfigOptions.ITEM_RENAME_COST, ConfigOptions.DEFAULT_ITEM_RENAME_COST), + Arguments.of(ConfigOptions.SACRIFICE_ILLEGAL_COST, ConfigOptions.DEFAULT_SACRIFICE_ILLEGAL_COST), + // Color options + Arguments.of(ConfigOptions.ALLOW_COLOR_CODE, ConfigOptions.DEFAULT_ALLOW_COLOR_CODE), + Arguments.of(ConfigOptions.ALLOW_HEXADECIMAL_COLOR, ConfigOptions.DEFAULT_ALLOW_HEXADECIMAL_COLOR), + Arguments.of(ConfigOptions.PERMISSION_NEEDED_FOR_COLOR, ConfigOptions.DEFAULT_PERMISSION_NEEDED_FOR_COLOR), + Arguments.of(ConfigOptions.USE_OF_COLOR_COST, ConfigOptions.DEFAULT_USE_OF_COLOR_COST) + + ); + } + + // Test every enchantment defaults + private static Stream provideForEnchantmentsTests() { + return RegistryAccess.registryAccess().getRegistry(RegistryKey.ENCHANTMENT) + .stream().map(enchantment -> Arguments.of(enchantment.getKey())); + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/mock/AnvilViewMock.java b/src/test/java/xyz/alexcrea/cuanvil/mock/AnvilViewMock.java new file mode 100644 index 0000000..cdb4c44 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/mock/AnvilViewMock.java @@ -0,0 +1,76 @@ +package xyz.alexcrea.cuanvil.mock; + +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.view.AnvilView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.mockbukkit.mockbukkit.inventory.PlayerInventoryViewMock; + +@SuppressWarnings({"removal"}) +public class AnvilViewMock extends PlayerInventoryViewMock implements AnvilView { + + private @NotNull AnvilInventory top; + + /** + * Constructs a new {@link PlayerInventoryViewMock} for the provided player, with the specified top inventory. + * + * @param player The player to create the view for. + * @param top The top inventory. + */ + public AnvilViewMock(@NotNull HumanEntity player, @NotNull AnvilInventory top) { + super(player, top); + this.top = top; + } + + @Override + public @Nullable String getRenameText() { + return top.getRenameText(); + } + + @Override + public int getRepairItemCountCost() { + return top.getRepairCostAmount(); + } + + @Override + public int getRepairCost() { + return top.getRepairCost(); + } + + @Override + public int getMaximumRepairCost() { + return top.getMaximumRepairCost(); + } + + @Override + public void setRepairItemCountCost(int amount) { + top.setRepairCostAmount(amount); + } + + @Override + public void setRepairCost(int cost) { + top.setRepairCost(cost); + } + + @Override + public void setMaximumRepairCost(int levels) { + top.setMaximumRepairCost(levels); + } + + @Override + public boolean bypassesEnchantmentLevelRestriction() { + throw new UnsupportedOperationException("Custom anvil was not think with this existing"); + } + + @Override + public void bypassEnchantmentLevelRestriction(boolean bypassEnchantmentLevelRestriction) { + throw new UnsupportedOperationException("Custom anvil was not think with this existing"); + } + + @Override + public @NotNull AnvilInventory getTopInventory() { + return top; + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/mock/EnchantedItemStackMock.java b/src/test/java/xyz/alexcrea/cuanvil/mock/EnchantedItemStackMock.java new file mode 100644 index 0000000..e7383f3 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/mock/EnchantedItemStackMock.java @@ -0,0 +1,57 @@ +package xyz.alexcrea.cuanvil.mock; + +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.mockbukkit.mockbukkit.inventory.ItemStackMock; + +public class EnchantedItemStackMock extends ItemStackMock { + + public EnchantedItemStackMock(@NotNull Material type, int amount) { + super(type, amount); + } + + public EnchantedItemStackMock(@NotNull Material type) { + this(type, 1); + } + + public EnchantedItemStackMock(@NotNull ItemStack stack) { + super(stack); + } + + @Override + public int removeEnchantment(@NotNull Enchantment ench) { + if(!this.hasItemMeta()) return 0; + + int level = this.getEnchantmentLevel(ench); + this.getItemMeta().removeEnchant(ench); + + return level; + } + + @Override + public void removeEnchantments() { + if(!this.hasItemMeta()) return; + + this.getItemMeta().removeEnchantments(); + } + + @Override + public boolean equals(Object obj) { + if(!super.equals(obj)) return false; + + return getItemMeta().equals(((ItemStack)obj).getItemMeta()); + } + + @Override + public @NotNull ItemStack clone() { + EnchantedItemStackMock clone = new EnchantedItemStackMock(this.getType()); + + clone.setAmount(this.getAmount()); + clone.setItemMeta(this.getItemMeta()); + clone.setDurability(this.getDurability()); + return clone; + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/tests/ConfigResetCustomAnvilTest.java b/src/test/java/xyz/alexcrea/cuanvil/tests/ConfigResetCustomAnvilTest.java new file mode 100644 index 0000000..3c03bb4 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/tests/ConfigResetCustomAnvilTest.java @@ -0,0 +1,38 @@ +package xyz.alexcrea.cuanvil.tests; + +import org.junit.jupiter.api.AfterEach; +import xyz.alexcrea.cuanvil.config.ConfigHolder; + +import java.io.File; + +public abstract class ConfigResetCustomAnvilTest extends DefaultCustomAnvilTest { + + @Override + @AfterEach + public void tearDown() { + // Destroy saved config file + String[] configs = new String[]{ + "config.yml", + "item_groups.yml", + "enchant_conflict.yml", + "unit_repair_item.yml", + "custom_recipes.yml" + }; + for (String config : configs) { + File configFile = new File(plugin.getDataFolder(), config); + configFile.delete(); + } + + // Set config to null + ConfigHolder.DEFAULT_CONFIG = null; + ConfigHolder.ITEM_GROUP_HOLDER = null; + ConfigHolder.CONFLICT_HOLDER = null; + ConfigHolder.UNIT_REPAIR_HOLDER = null; + ConfigHolder.CUSTOM_RECIPE_HOLDER = null; + + // Do parent works + super.tearDown(); + } + + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/tests/DefaultCustomAnvilTest.java b/src/test/java/xyz/alexcrea/cuanvil/tests/DefaultCustomAnvilTest.java new file mode 100644 index 0000000..3a1c399 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/tests/DefaultCustomAnvilTest.java @@ -0,0 +1,45 @@ +package xyz.alexcrea.cuanvil.tests; + +import io.delilaheve.CustomAnvil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.mockbukkit.mockbukkit.MockBukkit; +import org.mockbukkit.mockbukkit.ServerMock; +import xyz.alexcrea.cuanvil.enchant.CAEnchantment; +import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry; + +import java.util.ArrayList; +import java.util.List; + +public abstract class DefaultCustomAnvilTest { + + protected ServerMock server; + protected CustomAnvil plugin; + + @BeforeEach + public void setUp() { + // Start the mock server + server = MockBukkit.mock(); + // Load your plugin + plugin = MockBukkit.load(CustomAnvil.class); + // Continue initialization of the plugin + server.getScheduler().performOneTick(); + } + + @AfterEach + public void tearDown() { + // Stop the mock server + MockBukkit.unmock(); + + // Unregister enchantments + List toUnregister = new ArrayList<>( + CAEnchantmentRegistry.getInstance().values() + ); + + for (CAEnchantment caEnchantment : toUnregister) { + CAEnchantmentRegistry.getInstance().unregister(caEnchantment); + } + + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/tests/SharedCustomAnvilTest.java b/src/test/java/xyz/alexcrea/cuanvil/tests/SharedCustomAnvilTest.java new file mode 100644 index 0000000..02844f4 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/tests/SharedCustomAnvilTest.java @@ -0,0 +1,45 @@ +package xyz.alexcrea.cuanvil.tests; + +import io.delilaheve.CustomAnvil; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.mockbukkit.mockbukkit.MockBukkit; +import org.mockbukkit.mockbukkit.ServerMock; +import xyz.alexcrea.cuanvil.enchant.CAEnchantment; +import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry; + +import java.util.ArrayList; +import java.util.List; + +public abstract class SharedCustomAnvilTest { + + protected static ServerMock server; + protected static CustomAnvil plugin; + + @BeforeAll + public static void setUp() { + // Start the mock server + server = MockBukkit.mock(); + // Load your plugin + plugin = MockBukkit.load(CustomAnvil.class); + // Continue initialization of the plugin + server.getScheduler().performOneTick(); + } + + @AfterAll + public static void tearDown() { + // Stop the mock server + MockBukkit.unmock(); + + // Unregister enchantments + List toUnregister = new ArrayList<>( + CAEnchantmentRegistry.getInstance().values() + ); + + for (CAEnchantment caEnchantment : toUnregister) { + CAEnchantmentRegistry.getInstance().unregister(caEnchantment); + } + + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/tests/SharedOnlyMockBukkit.java b/src/test/java/xyz/alexcrea/cuanvil/tests/SharedOnlyMockBukkit.java new file mode 100644 index 0000000..0f38239 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/tests/SharedOnlyMockBukkit.java @@ -0,0 +1,24 @@ +package xyz.alexcrea.cuanvil.tests; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.mockbukkit.mockbukkit.MockBukkit; +import org.mockbukkit.mockbukkit.ServerMock; + +public class SharedOnlyMockBukkit { + + protected static ServerMock server; + + @BeforeAll + public static void setUp() { + // Start the mock server + server = MockBukkit.mock(); + } + + @AfterAll + public static void tearDown() { + // Stop the mock server + MockBukkit.unmock(); + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestData.java b/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestData.java new file mode 100644 index 0000000..2268d44 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestData.java @@ -0,0 +1,39 @@ +package xyz.alexcrea.cuanvil.util; + +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +public record AnvilFuseTestData( + @Nullable ItemStack leftItem, + @Nullable ItemStack rightItem, + @Nullable ItemStack expectedResult, + + @Nullable ItemStack expectedAfterLeftPlaced, + @Nullable ItemStack expectedAfterRightPlaced, + + @Nullable Integer expectedPriceAfterLeftPlaced, + @Nullable Integer expectedPriceAfterRightPlaced, + @Nullable Integer expectedPriceAfterBothPlaced + ){ + + public AnvilFuseTestData( + @Nullable ItemStack leftItem, + @Nullable ItemStack rightItem, + @Nullable ItemStack expectedResult, + @Nullable Integer expectedPriceAfterBothPlaced + ){ + this(leftItem, rightItem, expectedResult, + null, null, null, null, + expectedPriceAfterBothPlaced + ); + } + + public AnvilFuseTestData( + @Nullable ItemStack leftItem, + @Nullable ItemStack rightItem, + @Nullable ItemStack expectedResult + ){ + this(leftItem, rightItem, expectedResult, null + ); + } +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestUtil.java b/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestUtil.java new file mode 100644 index 0000000..de8a706 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestUtil.java @@ -0,0 +1,165 @@ +package xyz.alexcrea.cuanvil.util; + +import io.delilaheve.util.ItemUtil; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.PrepareAnvilEvent; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.Repairable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Assertions; +import xyz.alexcrea.cuanvil.enchant.CAEnchantment; +import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener; +import xyz.alexcrea.cuanvil.mock.AnvilViewMock; +import xyz.alexcrea.cuanvil.mock.EnchantedItemStackMock; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class AnvilFuseTestUtil { + + public static ItemStack prepareItem(@NotNull Material material, + @NotNull List enchantments, + @NotNull List level){ + return prepareItem(material, 0, enchantments, level); + } + + public static ItemStack prepareItem(@NotNull Material material, + int repairCost, + @NotNull List enchantments, + @NotNull List level){ + Assertions.assertEquals(enchantments.size(), level.size()); + + HashMap enchantmentMap = new HashMap<>(); + for (int i = 0; i < enchantments.size(); i++) { + enchantmentMap.put(enchantments.get(i), level.get(i)); + } + + ItemStack item = new EnchantedItemStackMock(material); + ItemUtil.INSTANCE.setEnchantmentsUnsafe(item, enchantmentMap); + + ItemMeta meta = item.getItemMeta(); + ((Repairable) meta).setRepairCost(repairCost); + item.setItemMeta(meta); + + return item; + } + + + public static ItemStack prepareItem(@NotNull Material material, + @NotNull List enchantmentNames, + Integer... levels){ + return prepareItem(material, 0, enchantmentNames, levels); + } + public static ItemStack prepareItem(@NotNull Material material, + int repairCost, + @NotNull List enchantmentNames, + Integer... levels){ + List enchantments = new ArrayList<>(); + + for (String enchantmentName : enchantmentNames) { + List enchantmentList = CAEnchantment.getListByName(enchantmentName); + Assertions.assertNotEquals(0, enchantmentList.size(), + "Could not find enchantment \"" + enchantmentName + "\""); + + enchantments.addAll(enchantmentList); + } + + return prepareItem(material, repairCost, enchantments, List.of(levels)); + } + + + /* + * Need to use that as it seems setting item in the inventory will not trigger the anvil click even + * + * Not the best for non-custom anvil plugins but work in the context of CA + */ + public static void imitateAnvilUpdate( + @NotNull HumanEntity player, + @NotNull AnvilInventory anvil) { + + AnvilViewMock view = new AnvilViewMock(player, anvil); + try { + PrepareAnvilEvent event = new PrepareAnvilEvent(view, anvil.getItem(2)); + + // Not ideal but possible and the easiest so why not + new PrepareAnvilListener().anvilCombineCheck(event); + anvil.setResult(event.getResult()); + } catch (Exception e){ + Assertions.fail(e); + } + } + + public static void executeAnvilTest( + @NotNull AnvilInventory anvil, + @NotNull HumanEntity player, + @NotNull AnvilFuseTestData data + ){ + Assertions.assertEquals(player.getOpenInventory().getTopInventory(), anvil, + "Openned inventory is not anvil"); + + // Test with only the left item + anvil.setItem(1, null); // We clear the right slot in case something was there + testPlacingItem(anvil, player, + 0, data.expectedPriceAfterLeftPlaced(), + data.leftItem(), data.expectedAfterLeftPlaced()); + + // Test with only the right item + anvil.setItem(0, null); // We only want the right item. so we remove the left one + testPlacingItem(anvil, player, + 1, data.expectedPriceAfterRightPlaced(), + data.rightItem(), data.expectedAfterRightPlaced()); + + // Test with both placed + testPlacingItem(anvil, player, + 0, data.expectedPriceAfterBothPlaced(), + data.leftItem(), data.expectedResult()); + + // Sadly, can't currently test player click + + } + + @SuppressWarnings({"removal"}) + private static void testPlacingItem( + @NotNull AnvilInventory anvil, + @NotNull HumanEntity player, + int slot, + Integer expectedPrice, + @Nullable ItemStack toPlace, + @Nullable ItemStack expectedResult){ + anvil.setItem(slot, toPlace); + anvil.setItem(2, null); + AnvilFuseTestUtil.imitateAnvilUpdate(player, anvil); + + ItemStack result = anvil.getItem(2); + assertEqual(expectedResult, result); + assertPriceEqual(expectedPrice, anvil.getRepairCost()); + } + + public static void assertEqual(@Nullable ItemStack item1, @Nullable ItemStack item2) { + boolean secondIsAir = isAir(item2); + if(isAir(item1)) Assertions.assertTrue(secondIsAir,"Item "+item2+" was not AIR but was expected to be air"); + else { + Assertions.assertFalse(secondIsAir,"Item "+item2+" was expected not to be air"); + + item1.setDurability(item1.getDurability()); + item2.setDurability(item2.getDurability()); + Assertions.assertEquals(item1, item2); + } + + } + + public static boolean isAir(@Nullable ItemStack item){ + return item == null || item.isEmpty(); + } + + public static void assertPriceEqual(Integer expectedPrice, int price){ + if(expectedPrice == null) return; + Assertions.assertEquals(price, expectedPrice); + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/util/CommonItemUtil.java b/src/test/java/xyz/alexcrea/cuanvil/util/CommonItemUtil.java new file mode 100644 index 0000000..1228cda --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/util/CommonItemUtil.java @@ -0,0 +1,27 @@ +package xyz.alexcrea.cuanvil.util; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class CommonItemUtil { + + public static ItemStack sharpness(int level){ + return AnvilFuseTestUtil.prepareItem( + Material.DIAMOND_SWORD, + List.of("sharpness"), + level + ); + } + + public static ItemStack bane_of_arthropods(int level){ + return AnvilFuseTestUtil.prepareItem( + Material.DIAMOND_SWORD, + List.of("bane_of_arthropods"), + level + ); + } + + +} diff --git a/src/test/resources/plugin.yml b/src/test/resources/plugin.yml new file mode 100644 index 0000000..a1f6983 --- /dev/null +++ b/src/test/resources/plugin.yml @@ -0,0 +1,61 @@ +main: io.delilaheve.CustomAnvil +name: CustomAnvil +prefix: "Custom Anvil" +version: test +folia-supported: true +description: Allow to customise anvil mechanics +api-version: 1.16 +load: POSTWORLD +authors: [ DelilahEve, alexcrea ] +libraries: + - org.jetbrains.kotlin:kotlin-stdlib:2.0.21 + +commands: + anvilconfigreload: + description: Reload every config of this plugin + permission: ca.command.reload + aliases: + #- acreload # anvil config reload + #- careload # custom anvil reload + - carl # custom anvil reload + customanvilconfig: + description: open a menu for administrator to edit plugin's config in game + permission: ca.config.edit + aliases: + - configanvil + +permissions: + ca.affected: + default: true + description: Player with this permission will be affected by the plugin + ca.bypass.fuse: + default: false + description: Allow player to combine every enchantments to every item (no custom limit) + ca.bypass.level: + default: false + description: Allow player to bypass every level limit (no custom limit) + ca.command.reload: + default: op + description: Allow administrator to reload the plugin's configs + ca.config.edit: + default: op + description: Allow administrator to edit the plugin's config in game + # color permissions + ca.color.code: + default: op + description: Allow player to use color code if permission is required (toggleable) + ca.color.hex: + default: op + description: Allow player to use hexadecimal color if permission is required (toggleable) + + +# soft depend on old name (UnsafeEnchantsPlus), so I can disable it if it is on the same server (old name for this plugin) +# Also depend to other plugin for compatibility +softdepend: + - UnsafeEnchantsPlus + - ProtocolLib + - Disenchantment + - EnchantsSquared + - EcoEnchants + - eco + - ExcellentEnchants