().configureEach {
+ sourceCompatibility = "21"
+ targetCompatibility = "21"
+
+ options.encoding = "UTF-8"
+}
+
+kotlin {
+ compilerOptions {
+ apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
+ jvmTarget.set(JvmTarget.JVM_21)
+ }
+}
diff --git a/nms/v1_21R7/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/packet/versions/V1_21R7_PacketManager.kt b/nms/v1_21R7/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/packet/versions/V1_21R7_PacketManager.kt
new file mode 100644
index 0000000..59ae9ce
--- /dev/null
+++ b/nms/v1_21R7/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/packet/versions/V1_21R7_PacketManager.kt
@@ -0,0 +1,33 @@
+package xyz.alexcrea.cuanvil.dependency.packet.versions
+
+import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket
+import net.minecraft.world.entity.player.Abilities
+import org.bukkit.craftbukkit.entity.CraftPlayer
+import org.bukkit.entity.Player
+import xyz.alexcrea.cuanvil.dependency.packet.PacketManager
+import xyz.alexcrea.cuanvil.dependency.packet.PacketManagerBase
+
+class V1_21R7_PacketManager : PacketManagerBase(), PacketManager {
+ override val canSetInstantBuild: Boolean
+ get() = true
+
+ override fun setInstantBuild(player: Player, instantBuild: Boolean) {
+ val nmsPlayer = (player as CraftPlayer).handle
+ val playerAbilities = nmsPlayer.abilities
+ val sendedAbilities: Abilities
+ if (playerAbilities.instabuild == instantBuild) {
+ sendedAbilities = playerAbilities
+ } else {
+ sendedAbilities = Abilities()
+ sendedAbilities.invulnerable = playerAbilities.invulnerable
+ sendedAbilities.flying = playerAbilities.flying
+ sendedAbilities.mayfly = playerAbilities.mayfly
+ sendedAbilities.instabuild = instantBuild
+ sendedAbilities.mayBuild = playerAbilities.mayBuild
+ sendedAbilities.flyingSpeed = playerAbilities.flyingSpeed
+ sendedAbilities.walkingSpeed = playerAbilities.walkingSpeed
+ }
+ val packet = ClientboundPlayerAbilitiesPacket(sendedAbilities)
+ nmsPlayer.connection.send(packet)
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index e661a74..9de7d8c 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,36 +1,22 @@
-import java.net.URI
-
rootProject.name = "CustomAnvil"
-// for Disenchantment dependency
-sourceControl {
- gitRepository(URI.create("https://github.com/H7KZ/Disenchantment.git")) {
- producesModule("cz.kominekjan:Disenchantment")
- }
-}
-
// NMS subproject
include("nms:nms-common")
findProject(":nms:nms-common")?.name = "nms-common"
-include("nms:v1_17R1")
-findProject(":nms:v1_17R1")?.name = "v1_17R1"
-include("nms:v1_18R1")
-findProject(":nms:v1_18R1")?.name = "v1_18R1"
-include("nms:v1_18R2")
-findProject(":nms:v1_18R2")?.name = "v1_18R2"
-include("nms:v1_19R1")
-findProject(":nms:v1_19R1")?.name = "v1_19R1"
-include("nms:v1_19R2")
-findProject(":nms:v1_19R2")?.name = "v1_19R2"
-include("nms:v1_19R3")
-findProject(":nms:v1_19R3")?.name = "v1_19R3"
-include("nms:v1_20R1")
-findProject(":nms:v1_20R1")?.name = "v1_20R1"
-include("nms:v1_20R2")
-findProject(":nms:v1_20R2")?.name = "v1_20R2"
-include("nms:v1_20R3")
-findProject(":nms:v1_20R3")?.name = "v1_20R3"
-include("nms:v1_20R4")
-findProject(":nms:v1_20R4")?.name = "v1_20R4"
-include("nms:v1_21R1")
-findProject(":nms:v1_21R1")?.name = "v1_21R1"
+include("nms:nms-paper")
+findProject(":nms:nms-paper")?.name = "nms-paper"
+
+
+val reobfNMS = providers.gradleProperty("subprojects.reobfnms")
+ .get().split(",")
+
+for (nmsPart in reobfNMS) {
+ include("nms:$nmsPart")
+ findProject(":nms:$nmsPart")?.name = nmsPart
+}
+
+// compatibility subprojects
+include(":impl:LegacyEcoEnchant")
+findProject(":impl:LegacyEcoEnchant")?.name = "LegacyEcoEnchant"
+include("impl:ExcellentEnchant5_4")
+findProject(":impl:ExcellentEnchant5_4")?.name = "ExcellentEnchant5_4"
\ No newline at end of file
diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilder.java b/src/main/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilder.java
index 74e8118..4292fa0 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilder.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/api/AnvilRecipeBuilder.java
@@ -14,7 +14,10 @@ public class AnvilRecipeBuilder {
private @NotNull String name;
private boolean exactCount;
- private int xpCostPerCraft;
+ private int levelCostPerCraft;
+ private int linearXpCostPerCraft;
+
+ private boolean removeExactLinearXp;
private @Nullable ItemStack leftItem;
private @Nullable ItemStack rightItem;
@@ -23,7 +26,7 @@ public class AnvilRecipeBuilder {
/**
* Instantiates a new Anvil recipe builder.
* exact count default to true.
- * xp cost per craft default to 1.
+ * xp level and linear cost per craft default to 0.
*
* @param name The recipe name
*/
@@ -31,7 +34,9 @@ public class AnvilRecipeBuilder {
this.name = name;
this.exactCount = true;
- this.xpCostPerCraft = 1;
+ this.levelCostPerCraft = 0;
+ this.linearXpCostPerCraft = 0;
+ this.removeExactLinearXp = false;
this.leftItem = null;
this.rightItem = null;
@@ -60,7 +65,7 @@ public class AnvilRecipeBuilder {
}
/**
- * Get if the recipe is exact count.
+ * Get if the recipe is exact count. (default 0)
*
* Exact count mean the recipe can only be crafted 1 by 1.
* If set to false, then it will craft as much as possible in 1 go and will keep unused material onto the anvil inventory.
@@ -86,12 +91,14 @@ public class AnvilRecipeBuilder {
}
/**
- * Get the xp level cost per craft.
+ * Get the xp level cost per craft. (default 0)
*
* @return The xp level cost per craft
+ * @deprecated use {@link #getLevelCostPerCraft() getLevelCostPerCraft} instead
*/
+ @Deprecated(since = "1.13.0")
public int getXpCostPerCraft() {
- return xpCostPerCraft;
+ return getLevelCostPerCraft();
}
/**
@@ -99,9 +106,78 @@ public class AnvilRecipeBuilder {
*
* @param xpCostPerCraft The xp level cost per craft
* @return This recipe builder instance.
+ * @deprecated use {@link #setLevelCostPerCraft(int) setLevelCostPerCraft} instead
*/
+ @Deprecated(since = "1.13.0")
public AnvilRecipeBuilder setXpCostPerCraft(int xpCostPerCraft) {
- this.xpCostPerCraft = xpCostPerCraft;
+ return setLevelCostPerCraft(xpCostPerCraft);
+ }
+
+ /**
+ * Get the xp level cost per craft. (default 0)
+ *
+ * @return The xp level cost per craft
+ */
+ public int getLevelCostPerCraft() {
+ return levelCostPerCraft;
+ }
+
+ /**
+ * Sets the xp level cost per craft.
+ *
+ * @param levelCostPerCraft The xp level cost per craft
+ * @return This recipe builder instance.
+ */
+ public AnvilRecipeBuilder setLevelCostPerCraft(int levelCostPerCraft) {
+ this.levelCostPerCraft = levelCostPerCraft;
+ return this;
+ }
+
+ /**
+ * Get the linear xp cost (not xp level cost) per craft.
+ *
+ * @return The xp level cost per craft
+ */
+ public int getLinearXpCostPerCraft() {
+ return linearXpCostPerCraft;
+ }
+
+ /**
+ * Sets the linear xp cost (not xp level cost) per craft.
+ *
+ * @param linearXpCostPerCraft The linear xp cost per craft
+ * @return This recipe builder instance.
+ */
+ public AnvilRecipeBuilder setLinearXpCostPerCraft(int linearXpCostPerCraft) {
+ this.linearXpCostPerCraft = linearXpCostPerCraft;
+ return this;
+ }
+
+ /**
+ * Get if the linear xp should get removed by an exact amount.
+ *
+ * If false (default) level cost will be the level that would be reached by a player with this amount of xp.
+ * If true will require the level that has at least the specified level of xp then on click remove only the necessary xp
+ *
+ * linear xp cost are applied after level cost
+ * @return if we should remove the exact amount of linear xp
+ */
+ public boolean isRemoveExactLinearXp() {
+ return removeExactLinearXp;
+ }
+
+ /**
+ * Set if the linear xp should get removed by an exact amount.
+ *
+ * If false (default) level cost will be the level that would be reached by a player with this amount of xp.
+ * If true will require the level that has at least the specified level of xp then on click remove only the necessary xp
+ *
+ * linear xp cost are applied after level cost
+ * @param removeExactLinearXp if we should remove the exact amount of linear xp
+ * @return This recipe builder instance.
+ */
+ public AnvilRecipeBuilder setRemoveExactLinearXp(boolean removeExactLinearXp) {
+ this.removeExactLinearXp = removeExactLinearXp;
return this;
}
@@ -182,12 +258,14 @@ 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,
this.exactCount,
- this.xpCostPerCraft,
+ this.levelCostPerCraft,
+ this.linearXpCostPerCraft,
+ this.removeExactLinearXp,
this.leftItem, this.rightItem, this.resultItem
);
}
@@ -198,7 +276,7 @@ public class AnvilRecipeBuilder {
*
* @return True if successful.
*/
- public boolean registerIfAbsent(){
+ public boolean registerIfAbsent() {
return CustomAnvilRecipeApi.addRecipe(this);
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/ConflictAPI.java b/src/main/java/xyz/alexcrea/cuanvil/api/ConflictAPI.java
index 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..1460766 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/api/ConflictBuilder.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/api/ConflictBuilder.java
@@ -13,6 +13,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
+//TODO add conflict after level
/**
* A Builder for material conflict.
*/
@@ -36,7 +37,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 +55,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 +64,7 @@ public class ConflictBuilder {
*
* @param name The conflict name
*/
- public ConflictBuilder(@NotNull String name){
+ public ConflictBuilder(@NotNull String name) {
this(name, null);
}
@@ -84,7 +85,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 +178,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 +190,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 +202,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 +214,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 +226,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 +238,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 +257,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 +277,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 +296,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 +316,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 +329,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 +346,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 +362,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 +385,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 +407,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 +417,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 +437,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/EnchantmentApi.java b/src/main/java/xyz/alexcrea/cuanvil/api/EnchantmentApi.java
index 32a62c3..ac98225 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/api/EnchantmentApi.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/api/EnchantmentApi.java
@@ -1,6 +1,7 @@
package xyz.alexcrea.cuanvil.api;
import io.delilaheve.CustomAnvil;
+import io.delilaheve.util.ConfigOptions;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.enchantments.Enchantment;
@@ -170,23 +171,37 @@ public class EnchantmentApi {
*/
public static boolean writeDefaultConfig(CAEnchantment enchantment, boolean override){
FileConfiguration config = ConfigHolder.DEFAULT_CONFIG.getConfig();
- if(!override && config.contains(enchantment.getName())) return false;
- writeDefaultConfig(config, enchantment);
-
- prepareSaveTask();
+ if(tryWriteDefaultConfig(config, enchantment, override)){
+ prepareSaveTask();
+ }
return true;
}
+ private static boolean tryWriteDefaultConfig(FileConfiguration defaultConfig, CAEnchantment enchantment, boolean override) {
+ boolean hasChange = false;
- private static void writeDefaultConfig(FileConfiguration defaultConfig, CAEnchantment enchantment) {
- defaultConfig.set("enchant_limits." + enchantment.getKey(), enchantment.defaultMaxLevel());
+ String levelPath = ConfigOptions.ENCHANT_LIMIT_ROOT + "." + enchantment.getKey();
+ if(override || !defaultConfig.isSet(levelPath)){
+ defaultConfig.set(levelPath, enchantment.defaultMaxLevel());
+ hasChange = true;
+ }
- String basePath = "enchant_values." + enchantment.getKey();
+ String basePath = ConfigOptions.ENCHANT_VALUES_ROOT + "." + enchantment.getKey();
EnchantmentRarity rarity = enchantment.defaultRarity();
- defaultConfig.set(basePath + ".item", rarity.getItemValue());
- defaultConfig.set(basePath + ".book", rarity.getBookValue());
+ String itemPath = basePath + ".item";
+ String bookPath = basePath + ".book";
+ if(override || !defaultConfig.isSet(itemPath)){
+ defaultConfig.set(itemPath, rarity.getItemValue());
+ hasChange = true;
+ }
+ if(override || !defaultConfig.isSet(bookPath)){
+ defaultConfig.set(bookPath, rarity.getBookValue());
+ hasChange = true;
+ }
+
+ return hasChange;
}
/**
diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/MaterialGroupApi.java b/src/main/java/xyz/alexcrea/cuanvil/api/MaterialGroupApi.java
index dd34eb6..cd71c7a 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/api/MaterialGroupApi.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/api/MaterialGroupApi.java
@@ -3,6 +3,7 @@ package xyz.alexcrea.cuanvil.api;
import io.delilaheve.CustomAnvil;
import io.delilaheve.util.ConfigOptions;
import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -22,49 +23,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 +80,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 +93,82 @@ 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) {
+ throw new UnsupportedOperationException("exclude group is temporarily disable for the time being. sorry");
+ // This code do not do what is intended ? idk why do it exist
+ //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 materialSet = group.getNonGroupInheritedMaterials();
Set groupSet = group.getGroups();
- config.set(basePath + ItemGroupManager.GROUP_TYPE_PATH, groupType);
- if(!materialSet.isEmpty()){
+ boolean empty = true;
+ if (!materialSet.isEmpty()) {
config.set(basePath + ItemGroupManager.MATERIAL_LIST_PATH, materialSetToStringList(materialSet));
+ empty = false;
+ } else {
+ config.set(basePath + ItemGroupManager.MATERIAL_LIST_PATH, null);
}
- if(!groupSet.isEmpty()){
- config.set(basePath + ItemGroupManager.GROUP_LIST_PATH, materialGroupSEtToStringList(groupSet));
+ if (!groupSet.isEmpty()) {
+ config.set(basePath + ItemGroupManager.GROUP_LIST_PATH, materialGroupSetToStringList(groupSet));
+ empty = false;
+ } else {
+ config.set(basePath + ItemGroupManager.GROUP_LIST_PATH, null);
}
+ if (empty) {
+ config.set(basePath + ItemGroupManager.GROUP_TYPE_PATH, null);
+ 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();
+ Set materials = group.getMaterials();
+
+ if (materials.isEmpty()) return false;
config.set(basePath + ItemGroupManager.GROUP_TYPE_PATH, "include");
- if(!materials.isEmpty()){
- config.set(basePath + ItemGroupManager.MATERIAL_LIST_PATH, materialSetToStringList(materials));
- }
+ config.set(basePath + ItemGroupManager.MATERIAL_LIST_PATH, materialSetToStringList(materials));
+ return true;
}
- public static List materialSetToStringList(@NotNull Set materials){
- return materials.stream().map(material -> material.getKey().getKey().toLowerCase()).toList();
+ public static List materialSetToStringList(@NotNull Set materials) {
+ return materials.stream().map(NamespacedKey::toString).toList();
}
- public static List materialGroupSEtToStringList(@NotNull Set groups){
+ public static List materialGroupSetToStringList(@NotNull Set groups) {
return groups.stream().map(AbstractMaterialGroup::getName).toList();
}
@@ -157,20 +178,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 +202,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 +214,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 +234,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 d471b19..bc50c16 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/api/UnitRepairApi.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/api/UnitRepairApi.java
@@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.dependency.DependencyManager;
import xyz.alexcrea.cuanvil.gui.config.global.UnitRepairConfigGui;
+import xyz.alexcrea.cuanvil.gui.config.list.MappedGuiListConfigGui;
import xyz.alexcrea.cuanvil.gui.config.list.UnitRepairElementListGui;
import java.util.ArrayList;
@@ -94,9 +95,9 @@ public class UnitRepairApi {
// Add to gui
UnitRepairConfigGui repairConfigGui = UnitRepairConfigGui.getCurrentInstance();
if(repairConfigGui != null) {
- UnitRepairElementListGui elementGui = repairConfigGui.getInstanceOrCreate(unit);
+ UnitRepairElementListGui elementGui = repairConfigGui.getInstanceOrCreate(unit).getStored();
- elementGui.updateValueForGeneric(repairableName, true);
+ if(elementGui != null) elementGui.updateValueForGeneric(repairableName, true);
repairConfigGui.updateValueForGeneric(unit, true);
}
@@ -116,22 +117,24 @@ 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;
if(config.isConfigurationSection(unitName.toLowerCase())) {
ConfigurationSection section = config.getConfigurationSection(unitName.toLowerCase());
- if(section.getKeys(false).isEmpty()) {
+
+ if(section != null && section.getKeys(false).isEmpty()) {
lastValue = true;
config.set(unitName.toLowerCase(), null);
}
} else if (config.isConfigurationSection(unitName.toUpperCase())) {
ConfigurationSection section = config.getConfigurationSection(unitName.toUpperCase());
- if(section.getKeys(false).isEmpty()) {
+ if(section != null && section.getKeys(false).isEmpty()) {
lastValue = true;
config.set(unitName.toUpperCase(), null);
}
@@ -140,15 +143,15 @@ 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
UnitRepairConfigGui repairConfigGui = UnitRepairConfigGui.getCurrentInstance();
if(repairConfigGui != null) {
- UnitRepairElementListGui elementGui = repairConfigGui.getInstanceOrCreate(unit);
+ UnitRepairElementListGui elementGui = repairConfigGui.getInstanceOrCreate(unit).getStored();
- elementGui.removeGeneric(repairableName);
+ if(elementGui != null) elementGui.removeGeneric(repairableName);
if(lastValue){
repairConfigGui.removeGeneric(unit);
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/CAConfigReadyEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/CAConfigReadyEvent.java
index 24691db..67d27a8 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/api/event/CAConfigReadyEvent.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/CAConfigReadyEvent.java
@@ -3,6 +3,23 @@ package xyz.alexcrea.cuanvil.api.event;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
+/**
+ * Called when the configuration of CustomAnvil is ready.
+ * It is called either on the plugin startup or on the plugin config reload.
+ *
+ * If you want to listen to the first trigger of this event (first configuration load. aka plugin load)
+ * you will need to register the listener on your plugin onEnable or earlier
+ *
+ * This event indicate that can start to register your recipes, item groups and conflicts.
+ * The vanilla and custom enchantments should already have been provided to CustomAnvil.
+ * Configuration can be changed any time after this event is triggered but never before.
+ *
+ * use {@link xyz.alexcrea.cuanvil.api.ConflictAPI ConflictApi},
+ * {@link xyz.alexcrea.cuanvil.gui.config.global.CustomRecipeConfigGui CustomRecipeConfigGui},
+ * {@link xyz.alexcrea.cuanvil.api.MaterialGroupApi MaterialGroupApi}
+ * and {@link xyz.alexcrea.cuanvil.api.UnitRepairApi UnitRepairApi}
+ * to add/remove/edit configurations
+ */
public class CAConfigReadyEvent extends Event {
private static final HandlerList HANDLERS = new HandlerList();
diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/CAEnchantRegistryReadyEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/CAEnchantRegistryReadyEvent.java
index 3e2fdf8..3ffe372 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/api/event/CAEnchantRegistryReadyEvent.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/CAEnchantRegistryReadyEvent.java
@@ -3,6 +3,17 @@ package xyz.alexcrea.cuanvil.api.event;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
+/**
+ * Called when custom anvil is ready to accept registration on custom enchantment.
+ *
+ * If you want to listen this event
+ * you will need to register the listener on your plugin onEnable or earlier
+ *
+ * Custom enchantments may be registered later but may cause issue if registered too later
+ * (after configuration loading phase. see {@link CAConfigReadyEvent})
+ *
+ * use {@link xyz.alexcrea.cuanvil.api.EnchantmentApi EnchantmentApi} to register and unregister your custom enchantments
+ */
public class CAEnchantRegistryReadyEvent extends Event {
private static final HandlerList HANDLERS = new HandlerList();
diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAClickResultBypassEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAClickResultBypassEvent.java
new file mode 100644
index 0000000..fe5e199
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAClickResultBypassEvent.java
@@ -0,0 +1,63 @@
+package xyz.alexcrea.cuanvil.api.event.listener;
+
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Called before custom anvil process the click on the result on the anvil inventory.
+ *
+ * This event is called after checking that the inventory is an anvil inventory and that the click is on the result slot
+ * but before checking if the player has the custom anvil affected permission.
+ *
+ * This event being cancelled will make CustomAnvil abort the click on result process.
+ *
+ * Most of the time you would likely need {@link CAPreAnvilBypassEvent} or {@link CAEarlyPreAnvilBypassEvent}
+ * for this event to be useful.
+ *
+ * There is also {@link CATreatAnvilResult2Event} that may be better for some use case.
+ */
+public class CAClickResultBypassEvent extends Event implements Cancellable {
+
+ private static final HandlerList HANDLERS = new HandlerList();
+
+ public static HandlerList getHandlerList() {
+ return HANDLERS;
+ }
+
+ @Override
+ public @NotNull HandlerList getHandlers() {
+ return HANDLERS;
+ }
+
+ private boolean cancelled = false;
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ @NotNull
+ private final InventoryClickEvent event;
+
+ /**
+ * Get the bukkit inventory click event causing to this event
+ *
+ * @return The click event causing to this event
+ */
+ @NotNull
+ public InventoryClickEvent getEvent() {
+ return event;
+ }
+
+ public CAClickResultBypassEvent(@NotNull InventoryClickEvent event) {
+ this.event = event;
+ }
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAEarlyPreAnvilBypassEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAEarlyPreAnvilBypassEvent.java
new file mode 100644
index 0000000..e92b4cd
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAEarlyPreAnvilBypassEvent.java
@@ -0,0 +1,63 @@
+package xyz.alexcrea.cuanvil.api.event.listener;
+
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.inventory.PrepareAnvilEvent;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Called before custom anvil process the prepare anvil event.
+ *
+ * This event will always get called when CustomAnvil need to handle
+ *
+ * This event being cancelled will make CustomAnvil abort the anvil process.
+ *
+ * You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful.
+ *
+ * It is also recommended that you read about {@link CAPreAnvilBypassEvent} and {@link CATreatAnvilResult2Event}
+ * as your use case may be more prone to use theses.
+ */
+public class CAEarlyPreAnvilBypassEvent extends Event implements Cancellable {
+
+ private static final HandlerList HANDLERS = new HandlerList();
+
+ public static HandlerList getHandlerList() {
+ return HANDLERS;
+ }
+
+ @Override
+ public @NotNull HandlerList getHandlers() {
+ return HANDLERS;
+ }
+
+ private boolean cancelled = false;
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ @NotNull
+ private final PrepareAnvilEvent event;
+
+ /**
+ * Get the bukkit pre anvil event causing this event
+ *
+ * @return The pre anvil event causing to this event
+ */
+ @NotNull
+ public PrepareAnvilEvent getEvent() {
+ return event;
+ }
+
+ public CAEarlyPreAnvilBypassEvent(@NotNull PrepareAnvilEvent event) {
+ this.event = event;
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAPreAnvilBypassEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAPreAnvilBypassEvent.java
new file mode 100644
index 0000000..9103a4b
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAPreAnvilBypassEvent.java
@@ -0,0 +1,66 @@
+package xyz.alexcrea.cuanvil.api.event.listener;
+
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.inventory.PrepareAnvilEvent;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Called before custom anvil process the prepare anvil event.
+ *
+ * This event is called after {@link CAEarlyPreAnvilBypassEvent},
+ * after checking that there is at least an item on the left slot
+ * and after checking if any of the 2 item is marked as immutable
+ * but before checking if the player has the custom anvil affected permission.
+ *
+ * This event being cancelled will make CustomAnvil abort the anvil process.
+ *
+ * You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful.
+ *
+ * It is also recommended that you read about {@link CAEarlyPreAnvilBypassEvent} and {@link CATreatAnvilResult2Event}
+ * as your use case may be more prone to use theses.
+ */
+public class CAPreAnvilBypassEvent extends Event implements Cancellable {
+
+ private static final HandlerList HANDLERS = new HandlerList();
+
+ public static HandlerList getHandlerList() {
+ return HANDLERS;
+ }
+
+ @Override
+ public @NotNull HandlerList getHandlers() {
+ return HANDLERS;
+ }
+
+ private boolean cancelled = false;
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ @NotNull
+ private final PrepareAnvilEvent event;
+
+ /**
+ * Get the bukkit pre anvil event causing this event
+ *
+ * @return The pre anvil event causing this event
+ */
+ @NotNull
+ public PrepareAnvilEvent getEvent() {
+ return event;
+ }
+
+ public CAPreAnvilBypassEvent(@NotNull PrepareAnvilEvent event) {
+ this.event = event;
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResult2Event.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResult2Event.java
new file mode 100644
index 0000000..30c5380
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResult2Event.java
@@ -0,0 +1,196 @@
+package xyz.alexcrea.cuanvil.api.event.listener;
+
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryView;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import xyz.alexcrea.cuanvil.anvil.AnvilCost;
+import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
+
+/**
+ * Called after custom anvil processed the click on the result on the anvil inventory.
+ * This event should be used to modify the result of an anvil use.
+ *
+ * You may also want to check {@link CAClickResultBypassEvent},
+ * {@link CAPreAnvilBypassEvent}
+ * and {@link CAEarlyPreAnvilBypassEvent} for your use case
+ *
+ * A null result will cancel this event
+ */
+@SuppressWarnings("unused")
+public class CATreatAnvilResult2Event extends Event {
+
+ private static final HandlerList HANDLERS = new HandlerList();
+
+ public static HandlerList getHandlerList() {
+ return HANDLERS;
+ }
+
+ @Override
+ public @NotNull HandlerList getHandlers() {
+ return HANDLERS;
+ }
+
+ @NotNull
+ private final InventoryView view;
+
+ private final AnvilUseType useType;
+
+ @Nullable
+ private final ItemStack left;
+ @Nullable
+ private final ItemStack right;
+
+ @Nullable
+ private ItemStack result;
+
+ private final AnvilCost cost;
+
+ @ApiStatus.Internal
+ public CATreatAnvilResult2Event(
+ @NotNull InventoryView view,
+ Inventory inv,
+ AnvilUseType useType,
+ @Nullable ItemStack result,
+ AnvilCost cost) {
+ this.view = view;
+ this.useType = useType;
+
+ this.left = inv.getItem(0); // TODO use view here
+ this.right = inv.getItem(1);
+ this.result = result;
+ this.cost = cost;
+ }
+
+ /**
+ * Get the bukkit inventory view.
+ *
+ * Temporarily marked as internal as it will get changed to anvil view on legacy removal
+ * so signature will change
+ *
+ * @return The inventory view of this event.
+ */
+ @ApiStatus.Internal
+ public @NotNull InventoryView getView() {
+ return view;
+ }
+
+
+ /**
+ * Get the type of use source of the result.
+ *
+ * @return The craft use type.
+ */
+ public AnvilUseType getUseType() {
+ return useType;
+ }
+
+ /**
+ * Get the left item of the anvil use
+ *
+ * @return the left item
+ */
+ public @Nullable ItemStack getLeftItem() {
+ return left;
+ }
+
+ /**
+ * Get the right item of the anvil use
+ *
+ * @return the right item
+ */
+ public @Nullable ItemStack getRightItem() {
+ return right;
+ }
+
+ /**
+ * Get the current result
+ *
+ * note that it will not be null unless another listener previously set it to null.
+ *
+ * @return The current result.
+ */
+ public @Nullable ItemStack getResult() {
+ return result;
+ }
+
+ /**
+ * Set the current result
+ *
+ * note that a null result will cancel this anvil use.
+ *
+ * @param result The new result
+ */
+ public void setResult(@Nullable ItemStack result) {
+ this.result = result;
+ }
+
+ /**
+ * Get the level cost displayed on the anvil.
+ *
Important note:
+ * the final price are re calculated on click for the following use case:
+ *
+ * - Custom craft
+ * - Unit repair
+ * - Lore edit
+ *
+ * This value will be used as final price for:
+ * Item merge
+ * Item rename
+ *
+ *
+ * @return The current cost.
+ * @deprecated use #{@link #getCost()} instead
+ */
+ @Deprecated(forRemoval = true, since = "1.17.0")
+ public int getLevelCost() {
+ return cost.asXpCost();
+ }
+
+ /**
+ * Set the level cost displayed on the anvil.
+ * Important note:
+ * the final price are re calculated on click for the following use case:
+ *
+ * - Custom craft
+ * - Unit repair
+ * - Lore edit
+ *
+ * This value will be used as final price for:
+ * Item merge
+ * Item rename
+ *
+ *
+ * @param levelCost The new cost.
+ * @deprecated use #{@link #getCost()} and set value on this instead
+ */
+ @Deprecated(forRemoval = true, since = "1.17.0")
+ public void setLevelCost(int levelCost) {
+ cost.setGeneric(levelCost - cost.getGeneric() - cost.asXpCost());
+ }
+
+ /**
+ * Allow access to the current cost of the event
+ * Note that modifying this object will change the event resulting cost
+ *
+ * Important note:
+ * the final price are re calculated on click for the following use case:
+ *
+ * - Custom craft
+ * - Unit repair
+ * - Lore edit
+ *
+ * This value will be used as final price for:
+ * Item merge
+ * Item rename
+ *
+ * @return the current anvil cost
+ */
+ public AnvilCost getCost() {
+ return cost;
+ }
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java
new file mode 100644
index 0000000..80965b5
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java
@@ -0,0 +1,162 @@
+package xyz.alexcrea.cuanvil.api.event.listener;
+
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.inventory.PrepareAnvilEvent;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import xyz.alexcrea.cuanvil.anvil.AnvilCost;
+import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
+
+/**
+ * Called after custom anvil processed the click on the result on the anvil inventory.
+ * This event should be used to modify the result of an anvil use.
+ *
+ * You may also want to check {@link CAClickResultBypassEvent},
+ * {@link CAPreAnvilBypassEvent}
+ * and {@link CAEarlyPreAnvilBypassEvent} for your use case
+ *
+ * A null result will cancel this pre anvil event
+ *
+ * @deprecated Prepare anvil Event cannot be provided as it can be called on result and therefore not have prepared anvil event
+ * use {@link CATreatAnvilResult2Event} instead
+ */
+@SuppressWarnings("unused")
+@Deprecated(forRemoval = true, since = "1.17.0")
+public class CATreatAnvilResultEvent extends Event {
+
+ private static final HandlerList HANDLERS = new HandlerList();
+
+ public static HandlerList getHandlerList() {
+ return HANDLERS;
+ }
+
+ @Override
+ public @NotNull HandlerList getHandlers() {
+ return HANDLERS;
+ }
+
+ @NotNull
+ private final PrepareAnvilEvent event;
+
+ private final AnvilUseType useType;
+
+ @Nullable
+ private ItemStack result;
+
+ private final AnvilCost cost;
+
+ public CATreatAnvilResultEvent(@NotNull PrepareAnvilEvent event, AnvilUseType useType, @Nullable ItemStack result, AnvilCost cost) {
+ this.event = event;
+ this.useType = useType;
+ this.result = result;
+ this.cost = cost;
+ }
+
+ /**
+ * Get the bukkit inventory click event causing to this event.
+ *
+ * @return The click event causing to this event.
+ */
+ public @NotNull PrepareAnvilEvent getEvent() {
+ return event;
+ }
+
+ /**
+ * Get the type of use source of the result.
+ *
+ * @return The craft use type.
+ */
+ public AnvilUseType getUseType() {
+ return useType;
+ }
+
+ /**
+ * Get the current result
+ *
+ * note that it will not be null unless another listener previously set it to null.
+ *
+ * @return The current result.
+ */
+ public @Nullable ItemStack getResult() {
+ return result;
+ }
+
+ /**
+ * Set the current result
+ *
+ * note that a null result will cancel this anvil use.
+ *
+ * @param result The new result
+ */
+ public void setResult(@Nullable ItemStack result) {
+ this.result = result;
+ }
+
+ /**
+ * Get the level cost displayed on the anvil.
+ *
Important note:
+ * the final price are re calculated on click for the following use case:
+ *
+ * - Custom craft
+ * - Unit repair
+ * - Lore edit
+ *
+ * This value will be used as final price for:
+ * Item merge
+ * Item rename
+ *
+ *
+ * @return The current cost.
+ * @deprecated use #{@link #getCost()} instead
+ */
+ @Deprecated(forRemoval = true, since = "1.17.0")
+ public int getLevelCost() {
+ return cost.asXpCost();
+ }
+
+ /**
+ * Set the level cost displayed on the anvil.
+ * Important note:
+ * the final price are re calculated on click for the following use case:
+ *
+ * - Custom craft
+ * - Unit repair
+ * - Lore edit
+ *
+ * This value will be used as final price for:
+ * Item merge
+ * Item rename
+ *
+ *
+ * @param levelCost The new cost.
+ * @deprecated use #{@link #getCost()} and set value on this instead
+ */
+ @Deprecated(forRemoval = true, since = "1.17.0")
+ public void setLevelCost(int levelCost) {
+ cost.setGeneric(levelCost - cost.getGeneric() - cost.asXpCost());
+ }
+
+ /**
+ * Allow access to the current cost of the event
+ * Note that modifying this object will change the event resulting cost
+ *
+ * Important note:
+ * the final price are re calculated on click for the following use case:
+ *
+ * - Custom craft
+ * - Unit repair
+ * - Lore edit
+ *
+ * This value will be used as final price for:
+ * Item merge
+ * Item rename
+ *
+ * @return the current anvil cost
+ */
+ public AnvilCost getCost() {
+ return cost;
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/config/ConfigHolder.java b/src/main/java/xyz/alexcrea/cuanvil/config/ConfigHolder.java
index 2037e23..f6a7e80 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/config/ConfigHolder.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/config/ConfigHolder.java
@@ -9,6 +9,7 @@ import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.group.EnchantConflictManager;
import xyz.alexcrea.cuanvil.group.ItemGroupManager;
import xyz.alexcrea.cuanvil.recipe.CustomAnvilRecipeManager;
+import xyz.alexcrea.cuanvil.util.MetricsUtil;
import java.io.File;
import java.io.IOException;
@@ -145,6 +146,7 @@ public abstract class ConfigHolder {
sufficientSuccess = true;
} catch (IOException e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "Could not copy backup saving config " + base.getName(), e);
+ MetricsUtil.INSTANCE.trackError(e);
}
}
// save last backup
@@ -275,6 +277,7 @@ public abstract class ConfigHolder {
this.deletedConfigFile.createNewFile();
} catch (IOException e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "Could not create " + this.deletedConfigFile.getPath(), e);
+ MetricsUtil.INSTANCE.trackError(e);
}
loadDeletedListFile(false);
@@ -312,6 +315,7 @@ public abstract class ConfigHolder {
this.deletedListConfig.save(this.deletedConfigFile);
} catch (IOException e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "Could not save " + this.deletedConfigFile.getPath(), e);
+ MetricsUtil.INSTANCE.trackError(e);
return false;
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/config/WorkPenaltyType.java b/src/main/java/xyz/alexcrea/cuanvil/config/WorkPenaltyType.java
index 95edca6..d374999 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/config/WorkPenaltyType.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/config/WorkPenaltyType.java
@@ -1,114 +1,66 @@
package xyz.alexcrea.cuanvil.config;
-import org.bukkit.Material;
-import org.bukkit.inventory.ItemStack;
-import org.bukkit.inventory.meta.ItemMeta;
-import org.jetbrains.annotations.NotNull;
+import com.google.common.collect.ImmutableMap;
import org.jetbrains.annotations.Nullable;
-import xyz.alexcrea.cuanvil.gui.config.settings.AbstractSettingGui;
-import xyz.alexcrea.cuanvil.gui.config.settings.EnumSettingGui;
+import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.EnumMap;
-public enum WorkPenaltyType implements EnumSettingGui.ConfigurableEnum {
- DEFAULT("default", true, true, "§aDefault", Material.LIME_TERRACOTTA),
- ADDITIVE("add_only", false, true, "§eAdd Only", Material.YELLOW_TERRACOTTA),
- INCREASE("increase_only", true, false, "§eIncrease Only", Material.YELLOW_TERRACOTTA),
- DISABLED("disabled", false, false, "§cDisabled", Material.RED_TERRACOTTA),
- ;
+public class WorkPenaltyType {
- private final String name;
- private final boolean penaltyIncrease;
- private final boolean penaltyAdditive;
+ public record WorkPenaltyPart(
+ boolean penaltyIncrease,
+ boolean penaltyAdditive,
+ boolean exclusivePenaltyIncrease,
+ boolean exclusivePenaltyAdditive
+ ) {
- private final String configName;
- private final Material configMaterial;
+ @Override
+ public boolean equals(Object obj) {
+ if(!(obj instanceof WorkPenaltyPart other)) return false;
- WorkPenaltyType(String name, boolean penaltyIncrease, boolean penaltyAdditive, String configName, Material configMaterial) {
- this.name = name;
- this.penaltyIncrease = penaltyIncrease;
- this.penaltyAdditive = penaltyAdditive;
- this.configName = configName;
- this.configMaterial = configMaterial;
- }
-
- public boolean isPenaltyIncreasing() {
- return penaltyIncrease;
- }
-
- public boolean isPenaltyAdditive() {
- return penaltyAdditive;
- }
-
- private boolean doRepresentThisType(String toTest){
- return name.equalsIgnoreCase(toTest);
- }
-
- @NotNull
- public static WorkPenaltyType fromString(@Nullable String toTest){
- if(toTest == null) return DEFAULT;
-
- // Test if it matches any of values
- for (WorkPenaltyType value : values()) {
- if(value.doRepresentThisType(toTest)){
- return value;
- }
+ return other.penaltyIncrease == this.penaltyIncrease &&
+ other.penaltyAdditive == this.penaltyAdditive &&
+ other.exclusivePenaltyIncrease == this.exclusivePenaltyIncrease &&
+ other.exclusivePenaltyAdditive == this.exclusivePenaltyAdditive;
}
- // Use default if not found
- return DEFAULT;
+ public WorkPenaltyPart(boolean penaltyIncrease, boolean penaltyAdditive) {
+ this(penaltyIncrease, penaltyAdditive, false, false);
+ }
}
- @NotNull
- public static WorkPenaltyType next(@NotNull WorkPenaltyType now){
- return switch (now){
- case DEFAULT -> ADDITIVE;
- case ADDITIVE -> INCREASE;
- case INCREASE -> DISABLED;
- case DISABLED -> DEFAULT;
+ private final EnumMap partMap;
- };
+ public WorkPenaltyType(@Nullable EnumMap partMap) {
+ this.partMap = new EnumMap<>(partMap != null ? partMap : new EnumMap<>(AnvilUseType.class));
+ }
+ public ImmutableMap getPartMap() {
+ return ImmutableMap.copyOf(partMap);
+ }
+
+ public WorkPenaltyPart getPenaltyInfo(AnvilUseType type) {
+ return partMap.getOrDefault(type, type.getDefaultPenalty());
+ }
+
+ public boolean isPenaltyIncreasing(AnvilUseType type) {
+ return partMap.getOrDefault(type, type.getDefaultPenalty()).penaltyIncrease;
+ }
+
+ public boolean isPenaltyAdditive(AnvilUseType type) {
+ return partMap.getOrDefault(type, type.getDefaultPenalty()).penaltyAdditive;
}
@Override
- public ItemStack configurationGuiItem() {
- ItemStack displayedItem = new ItemStack(this.configMaterial);
- ItemMeta valueMeta = displayedItem.getItemMeta();
- assert valueMeta != null;
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof WorkPenaltyType that)) return false;
- valueMeta.setDisplayName(this.configName);
-
- List lore = new ArrayList<>();
-
- lore.add(configDisplayForAdd());
- lore.add(configDisplayForIncrease());
- lore.add("");
-
- lore.add(AbstractSettingGui.CLICK_LORE);
- valueMeta.setLore(lore);
-
- displayedItem.setItemMeta(valueMeta);
-
- return displayedItem;
+ for (AnvilUseType type : AnvilUseType.getEntries()) {
+ if(!getPenaltyInfo(type).equals(that.getPenaltyInfo(type))) return false;
+ }
+ return true;
}
- public String configDisplayForAdd(){
- return ("§7Add penalty: " + (penaltyAdditive ? "§aYes" : "§cNo"));
- }
-
- public String configDisplayForIncrease(){
- return ("§7Increase penalty: " + (penaltyIncrease ? "§aYes" : "§cNo"));
- }
-
- @Override
- public String configName() {
- return this.name;
- }
-
- @Override
- public String configurationGuiName() {
- return this.configName;
- }
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/AdditionalTestEnchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/AdditionalTestEnchantment.java
index 832e5af..821838f 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/enchant/AdditionalTestEnchantment.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/AdditionalTestEnchantment.java
@@ -1,6 +1,7 @@
package xyz.alexcrea.cuanvil.enchant;
import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@@ -11,24 +12,23 @@ public interface AdditionalTestEnchantment {
/**
* Test if the provided enchantments can be compatible with this enchantment. only non-Custom Anvil conflict.
* @param enchantments Immutable map of validated enchantments for the item.
- * @param itemMat Material of the tested item.
+ * @param itemType Material namespaced key of the tested item.
* @return If there is a conflict with the enchantments.
*/
boolean isEnchantConflict(
@NotNull Map enchantments,
- @NotNull Material itemMat);
-
+ @NotNull NamespacedKey itemType);
/**
* Test if the provided item can be compatible with this enchantment. only non-Custom Anvil conflict.
* @param enchantments Immutable map of validated enchantments for the item.
- * @param itemMat Material of the tested item.
+ * @param itemType Material namespaced key of the tested item.
* @param item Provide a new instance of the used item stack with the partial enchantment applied.
* @return If there is a conflict with the enchantment and the item.
*/
boolean isItemConflict(
@NotNull Map enchantments,
- @NotNull Material itemMat,
+ @NotNull NamespacedKey itemType,
@NotNull ItemStack item);
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantment.java
index 1d94ba2..ea657ac 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantment.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantment.java
@@ -207,7 +207,6 @@ public interface CAEnchantment {
@NotNull ItemMeta meta,
@NotNull Map enchantments,
@NotNull Collection enchantmentToTest){
-
for (CAEnchantment enchantment : enchantmentToTest) {
if(enchantment.isEnchantmentPresent(item, meta)){
enchantments.put(enchantment, enchantment.getLevel(item, meta));
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java
index 1225209..854ed55 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/CAEnchantmentRegistry.java
@@ -5,10 +5,12 @@ import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.enchant.bulk.BukkitEnchantBulkOperation;
import xyz.alexcrea.cuanvil.enchant.bulk.BulkCleanEnchantOperation;
import xyz.alexcrea.cuanvil.enchant.bulk.BulkGetEnchantOperation;
import xyz.alexcrea.cuanvil.enchant.wrapped.CABukkitEnchantment;
+import xyz.alexcrea.cuanvil.util.MetricsUtil;
import java.util.*;
import java.util.logging.Level;
@@ -16,6 +18,7 @@ import java.util.logging.Level;
public class CAEnchantmentRegistry {
private static final CAEnchantmentRegistry instance = new CAEnchantmentRegistry();
+
public static CAEnchantmentRegistry getInstance() {
return instance;
}
@@ -49,7 +52,7 @@ public class CAEnchantmentRegistry {
* This should only be called on main of custom anvil.
* If called more than one time, chance of thing being broken will be high.
*/
- public void registerBukkit(){
+ public void registerBukkit() {
// Register enchantment
for (Enchantment enchantment : Enchantment.values()) {
register(new CABukkitEnchantment(enchantment));
@@ -59,7 +62,6 @@ public class CAEnchantmentRegistry {
BukkitEnchantBulkOperation bukkitOperation = new BukkitEnchantBulkOperation();
optimisedGetOperators.add(bukkitOperation);
optimisedCleanOperators.add(bukkitOperation);
-
}
private static boolean hasWarnedRegistering = false;
@@ -69,20 +71,32 @@ public class CAEnchantmentRegistry {
*
* No guarantee that the enchantment will be present on the config gui if registered late.
* (By late I mean after custom anvil startup.)
+ *
* @param enchantment The enchantment to be registered.
* @return If the operation was successful.
*/
- public boolean register(@NotNull CAEnchantment enchantment){
- if(byKeyMap.containsKey(enchantment.getKey())){
+ public boolean register(@NotNull CAEnchantment enchantment) {
+ if (byKeyMap.containsKey(enchantment.getKey())) {
+ if (Objects.equals(enchantment, byKeyMap.get(enchantment.getKey()))) {
+ // We are trying to register the exact same enchantment. so we just skip it.
+ return false;
+ }
+
+ if (ConfigHolder.DEFAULT_CONFIG.getConfig().getBoolean("caution_secret_do_not_log_duplicated_registered_key", false)) {
+ return false;
+ }
+
+ var error = new IllegalStateException("enchantment " + enchantment.getKey() + " was already registered");
CustomAnvil.instance.getLogger().log(Level.WARNING,
- "Duplicate registered enchantment. This should NOT happen any time.\n" +
- "If you are a custom anvil developer. You maybe custom anvil detected your enchantment as a bukkit enchantment. " +
- "maybe remove enchantment with the same key before registering yours",
- new IllegalStateException(enchantment.getKey()+" enchantment was already registered"));
+ "Duplicate distinct registered enchantment. This should NOT happen any time.\n" +
+ "If you are a custom anvil developer: Maybe custom anvil detected your enchantment as a bukkit enchantment. " +
+ "you should maybe remove enchantment with the same key before registering yours",
+ error);
+ MetricsUtil.INSTANCE.trackError(error);
return false;
}
- if((!hasWarnedRegistering) && byNameMap.containsKey(enchantment.getName())){
+ if ((!hasWarnedRegistering) && byNameMap.containsKey(enchantment.getName())) {
hasWarnedRegistering = true;
CustomAnvil.instance.getLogger().log(Level.WARNING,
@@ -96,10 +110,10 @@ public class CAEnchantmentRegistry {
nameSortedEnchantments.add(enchantment);
- if(!enchantment.isGetOptimised()){
+ if (!enchantment.isGetOptimised()) {
unoptimisedGetValues.add(enchantment);
}
- if(!enchantment.isCleanOptimised()){
+ if (!enchantment.isCleanOptimised()) {
unoptimisedCleanValues.add(enchantment);
}
@@ -113,12 +127,13 @@ public class CAEnchantmentRegistry {
*
* No guarantee that the enchantment will absent if the config guis if unregistered late.
* (By late I mean after custom anvil startup.)
+ *
* @param enchantment The enchantment to be unregistered.
* @return If the operation was successful.
*/
- public boolean unregister(@Nullable CAEnchantment enchantment){
- if(enchantment == null) return false;
+ public boolean unregister(@Nullable CAEnchantment enchantment) {
+ if (enchantment == null) return false;
byKeyMap.remove(enchantment.getKey());
byNameMap.get(enchantment.getName()).remove(enchantment);
@@ -131,42 +146,45 @@ public class CAEnchantmentRegistry {
/**
* Gets the enchantment by the provided key.
+ *
* @param key Key to fetch.
* @return Registered enchantment. null if absent.
*/
@Nullable
- public CAEnchantment getByKey(@NotNull NamespacedKey key){
+ public CAEnchantment getByKey(@NotNull NamespacedKey key) {
return byKeyMap.get(key);
}
/**
* Gets the enchantment by the provided name.
+ *
* @param name Name to fetch.
* @return Registered enchantment. null if absent.
- *
* @deprecated use {@link #getListByName(String)}
*/
@Deprecated(since = "1.6.3")
@Nullable
- public CAEnchantment getByName(@NotNull String name){
+ public CAEnchantment getByName(@NotNull String name) {
List enchantments = getListByName(name);
- if(enchantments.isEmpty()) return null;
+ if (enchantments.isEmpty()) return null;
return enchantments.get(0);
}
/**
* Gets list of enchantment using the provided name.
+ *
* @param name Name to fetch.
* @return List of registered enchantment.
*/
@NotNull
- public List getListByName(@NotNull String name){
+ public List getListByName(@NotNull String name) {
return byNameMap.getOrDefault(name, Collections.emptyList());
}
/**
* Gets an array of all the registered enchantments.
+ *
* @return Array of enchantments.
*/
@NotNull
@@ -176,6 +194,7 @@ public class CAEnchantmentRegistry {
/**
* Gets a map of all the registered enchantments.
+ *
* @return Immutable map of enchantments.
*/
public Map registeredEnchantments() {
@@ -184,6 +203,7 @@ public class CAEnchantmentRegistry {
/**
* Gets a list of all the unoptimised get operation enchantments.
+ *
* @return List of unoptimised enchantments.
*/
@NotNull
@@ -193,6 +213,7 @@ public class CAEnchantmentRegistry {
/**
* Gets a list of all the unoptimised clean operation enchantments.
+ *
* @return List of unoptimised enchantments.
*/
@NotNull
@@ -202,6 +223,7 @@ public class CAEnchantmentRegistry {
/**
* Get "clean optimised operation" for get enchantments.
+ *
* @return Mutable "clean enchantments optimised operation" list.
*/
public List getOptimisedCleanOperators() {
@@ -210,6 +232,7 @@ public class CAEnchantmentRegistry {
/**
* Get "get optimised operation" for get enchantments.
+ *
* @return Mutable "get enchantments optimised operation" list.
*/
public List getOptimisedGetOperators() {
@@ -218,6 +241,7 @@ public class CAEnchantmentRegistry {
/**
* Get custom anvil enchantment sorted by name.
+ *
* @return An immutable sorted set of every registered enchantment sorted by name.
*/
public SortedSet getNameSortedEnchantments() {
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..73e4185 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java
@@ -1,7 +1,10 @@
package xyz.alexcrea.cuanvil.enchant.bulk;
+import io.delilaheve.CustomAnvil;
+import io.delilaheve.util.ConfigOptions;
import io.delilaheve.util.ItemUtil;
import org.bukkit.Material;
+import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.inventory.meta.ItemMeta;
@@ -14,22 +17,37 @@ import java.util.Map;
public class BukkitEnchantBulkOperation implements BulkGetEnchantOperation, BulkCleanEnchantOperation {
@Override
- public void bulkGet(@NotNull Map enchantmentList, @NotNull ItemStack item, @NotNull ItemMeta meta) {
- if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
- ((EnchantmentStorageMeta)meta).getStoredEnchants().forEach((enchantment, level) ->
- enchantmentList.put(EnchantmentApi.getByKey(enchantment.getKey()), level)
+ public void bulkGet(@NotNull Map enchantmentMap, @NotNull ItemStack item, @NotNull ItemMeta meta) {
+ boolean isBook = ItemUtil.INSTANCE.isEnchantedBook(item);
+
+ if (isBook) {
+ ((EnchantmentStorageMeta) meta).getStoredEnchants().forEach((enchantment, level) ->
+ addEnchantment(enchantmentMap, enchantment, level)
);
- } else {
+ }
+ if(!isBook || ConfigOptions.INSTANCE.getAddBookEnchantmentAsStoredEnchantment()){
item.getEnchantments().forEach((enchantment, level) ->
- enchantmentList.put(EnchantmentApi.getByKey(enchantment.getKey()), level)
+ addEnchantment(enchantmentMap, enchantment, level)
);
}
}
+ public void addEnchantment(@NotNull Map enchantmentMap, @NotNull Enchantment enchantment, int level) {
+ CAEnchantment enchant = EnchantmentApi.getByKey(enchantment.getKey());
+ if (enchant == null) {
+ CustomAnvil.instance.getLogger().warning("Enchantment of key " + enchantment.getKey() +
+ " somehow not found in CustomAnvil ?");
+ return;
+ }
+
+ enchantmentMap.put(enchant, level);
+ }
+
@Override
public void bulkClear(@NotNull ItemStack item) {
- if (item.getType() != Material.ENCHANTED_BOOK) {
- item.getEnchantments().forEach((enchantment, leve) ->
+ if (item.getType() != Material.ENCHANTED_BOOK || ConfigOptions.INSTANCE.getAddBookEnchantmentAsStoredEnchantment()) {
+
+ item.getEnchantments().forEach((enchantment, level) ->
item.removeEnchantment(enchantment)
);
}
@@ -43,5 +61,6 @@ public class BukkitEnchantBulkOperation implements BulkGetEnchantOperation, Bulk
bookMeta.removeStoredEnchant(enchantment)
);
}
+
}
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BulkGetEnchantOperation.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BulkGetEnchantOperation.java
index a985edd..7c66e8a 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BulkGetEnchantOperation.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BulkGetEnchantOperation.java
@@ -14,10 +14,10 @@ public interface BulkGetEnchantOperation {
/**
* Bulk get part of the stored enchantment of this item.
- * @param enchantmentList Mutable map of collected enchantment. should b
+ * @param enchantmentMap Mutable map of collected enchantment. should b
* @param item The item to get enchantment from. Should not get edited.
* @param meta The item meta to get enchantment from. Should not get edited.
*/
- void bulkGet(@NotNull Map enchantmentList, @NotNull ItemStack item, @NotNull ItemMeta meta);
+ void bulkGet(@NotNull Map enchantmentMap, @NotNull ItemStack item, @NotNull ItemMeta meta);
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/EnchantSquaredBulkOperation.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/EnchantSquaredBulkOperation.java
index 59bd6ec..57ecf60 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/EnchantSquaredBulkOperation.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/EnchantSquaredBulkOperation.java
@@ -5,7 +5,7 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.dependency.DependencyManager;
-import xyz.alexcrea.cuanvil.dependency.EnchantmentSquaredDependency;
+import xyz.alexcrea.cuanvil.dependency.plugins.EnchantmentSquaredDependency;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
import java.util.Collections;
@@ -14,10 +14,10 @@ import java.util.Map;
public class EnchantSquaredBulkOperation implements BulkGetEnchantOperation, BulkCleanEnchantOperation {
@Override
- public void bulkGet(@NotNull Map enchantmentList, @NotNull ItemStack item, @NotNull ItemMeta meta) {
+ public void bulkGet(@NotNull Map enchantmentMap, @NotNull ItemStack item, @NotNull ItemMeta meta) {
EnchantmentSquaredDependency enchantmentSquared = DependencyManager.INSTANCE.getEnchantmentSquaredCompatibility();
if(enchantmentSquared != null){
- enchantmentSquared.getEnchantmentsSquared(item, enchantmentList);
+ enchantmentSquared.getEnchantmentsSquared(item, enchantmentMap);
}
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/SuperEnchantBulkOperation.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/SuperEnchantBulkOperation.java
new file mode 100644
index 0000000..8bc729a
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/SuperEnchantBulkOperation.java
@@ -0,0 +1,47 @@
+package xyz.alexcrea.cuanvil.enchant.bulk;
+
+import com.maddoxh.superEnchants.items.EnchantApplicator;
+import com.maddoxh.superEnchants.items.EnchantReader;
+import io.delilaheve.CustomAnvil;
+import org.bukkit.NamespacedKey;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+import xyz.alexcrea.cuanvil.api.EnchantmentApi;
+import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
+
+import java.util.Map;
+
+public class SuperEnchantBulkOperation implements BulkGetEnchantOperation, BulkCleanEnchantOperation {
+
+ private Plugin plugin;
+ public SuperEnchantBulkOperation(Plugin plugin) {
+ this.plugin = plugin;
+ }
+
+ @Override
+ public void bulkGet(@NotNull Map enchantmentMap, @NotNull ItemStack item, @NotNull ItemMeta meta) {
+ EnchantReader.INSTANCE.readEnchants(item).forEach((ench, level) -> {
+ var enchantment = EnchantmentApi.getByKey(NamespacedKey.fromString(ench, plugin));
+ if(enchantment == null) {
+ CustomAnvil.log("Enchantment " + ench + " not found in custom anvil");
+ return;
+ }
+
+ enchantmentMap.put(enchantment, level);
+ }
+ );
+ }
+
+ @Override
+ public void bulkClear(@NotNull ItemStack item) {
+ EnchantApplicator.INSTANCE.clearAllCustomEnchants(item);
+ }
+
+ @Override
+ public void bulkClear(@NotNull ItemStack item, @NotNull ItemMeta meta) {
+ // item meta is not preferred for enchantment squared clear
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CABukkitEnchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CABukkitEnchantment.java
index 6b062aa..0e630ea 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CABukkitEnchantment.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CABukkitEnchantment.java
@@ -1,6 +1,7 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import io.delilaheve.CustomAnvil;
+import io.delilaheve.util.ConfigOptions;
import io.delilaheve.util.ItemUtil;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.enchantments.EnchantmentTarget;
@@ -18,6 +19,7 @@ import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.logging.Level;
/**
@@ -25,17 +27,17 @@ import java.util.logging.Level;
*/
public class CABukkitEnchantment extends CAEnchantmentBase {
- private final @NotNull Enchantment enchantment;
+ public final @NotNull Enchantment bukkit;
- public CABukkitEnchantment(@NotNull Enchantment enchantment, @Nullable EnchantmentRarity rarity){
- super(enchantment.getKey(),
+ public CABukkitEnchantment(@NotNull Enchantment bukkit, @Nullable EnchantmentRarity rarity) {
+ super(bukkit.getKey(),
rarity,
- enchantment.getMaxLevel());
- this.enchantment = enchantment;
+ bukkit.getMaxLevel());
+ this.bukkit = bukkit;
}
- public CABukkitEnchantment(@NotNull Enchantment enchantment){
- this(enchantment, getRarity(enchantment));
+ public CABukkitEnchantment(@NotNull Enchantment bukkit) {
+ this(bukkit, getRarity(bukkit));
}
@Override
@@ -51,33 +53,34 @@ public class CABukkitEnchantment extends CAEnchantmentBase {
@Override
public int getLevel(@NotNull ItemStack item, @NotNull ItemMeta meta) {
if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
- return ((EnchantmentStorageMeta)meta).getStoredEnchantLevel(this.enchantment);
+ return ((EnchantmentStorageMeta) meta).getStoredEnchantLevel(this.bukkit);
} else {
- return meta.getEnchantLevel(this.enchantment);
+ return meta.getEnchantLevel(this.bukkit);
}
}
@Override
public boolean isEnchantmentPresent(@NotNull ItemStack item, @NotNull ItemMeta meta) {
if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
- EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta)meta);
+ EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta) meta);
- return bookMeta.getStoredEnchants().containsKey(this.enchantment);
- }else{
- return item.containsEnchantment(this.enchantment);
+ return bookMeta.getStoredEnchants().containsKey(this.bukkit) ||
+ (ConfigOptions.INSTANCE.getAddBookEnchantmentAsStoredEnchantment() && item.containsEnchantment(this.bukkit));
+ } else {
+ return item.containsEnchantment(this.bukkit);
}
}
@Override
public void addEnchantmentUnsafe(@NotNull ItemStack item, int level) {
if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
- EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta)item.getItemMeta());
+ EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta) item.getItemMeta());
assert bookMeta != null;
- bookMeta.addStoredEnchant(this.enchantment, level, true);
+ bookMeta.addStoredEnchant(this.bukkit, level, true);
item.setItemMeta(bookMeta);
} else {
- item.addUnsafeEnchantment(this.enchantment, level);
+ item.addUnsafeEnchantment(this.bukkit, level);
}
}
@@ -85,19 +88,20 @@ public class CABukkitEnchantment extends CAEnchantmentBase {
@Override
public void removeFrom(@NotNull ItemStack item) {
if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
- EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta)item.getItemMeta());
+ EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta) item.getItemMeta());
assert bookMeta != null;
- bookMeta.removeStoredEnchant(this.enchantment);
+ bookMeta.removeStoredEnchant(this.bukkit);
+ bookMeta.removeEnchant(this.bukkit);
item.setItemMeta(bookMeta);
- }else{
- item.removeEnchantment(this.enchantment);
+ } else {
+ item.removeEnchantment(this.bukkit);
}
}
@NotNull
- public static EnchantmentRarity getRarity(Enchantment enchantment){
+ public static EnchantmentRarity getRarity(Enchantment enchantment) {
try {
return EnchantmentProperties.valueOf(enchantment.getKey().getKey().toUpperCase(Locale.ENGLISH)).getRarity();
} catch (IllegalArgumentException ignored) {
@@ -107,10 +111,11 @@ public class CABukkitEnchantment extends CAEnchantmentBase {
@NotNull
protected Enchantment getEnchant() {
- return this.enchantment;
+ return this.bukkit;
}
private static Method getAnvilCostMethod;
+
static {
Class clazz = Enchantment.class;
try {
@@ -143,18 +148,27 @@ public class CABukkitEnchantment extends CAEnchantmentBase {
}
private static EnchantmentRarity findRarity(Enchantment enchantment) {
- if(getAnvilCostMethod == null) return EnchantmentRarity.COMMON;
+ if (getAnvilCostMethod == null) return EnchantmentRarity.COMMON;
try {
int itemCost = (int) getAnvilCostMethod.invoke(enchantment);
return EnchantmentRarity.getRarity(itemCost);
} catch (IllegalAccessException | InvocationTargetException e) {
- CustomAnvil.instance.getLogger().log(Level.SEVERE, "could not find cost for enchantment "+enchantment.getKey(), e);
+ CustomAnvil.instance.getLogger().log(Level.SEVERE, "could not find cost for enchantment " + enchantment.getKey(), e);
return EnchantmentRarity.COMMON;
}
}
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CABukkitEnchantment other)) {
+ return false;
+ }
+
+ return Objects.equals(this.bukkit, other.getEnchant());
+ }
+
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEEEnchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEEEnchantment.java
deleted file mode 100644
index dfe60ff..0000000
--- a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEEEnchantment.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package xyz.alexcrea.cuanvil.enchant.wrapped;
-
-import org.bukkit.Material;
-import org.bukkit.inventory.ItemStack;
-import org.jetbrains.annotations.NotNull;
-import su.nightexpress.excellentenchants.api.enchantment.CustomEnchantment;
-import su.nightexpress.excellentenchants.api.enchantment.Definition;
-import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
-import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
-import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
-
-import java.util.Map;
-import java.util.Set;
-
-public class CAEEEnchantment extends CABukkitEnchantment implements AdditionalTestEnchantment {
-
- @NotNull CustomEnchantment eeenchantment;
- @NotNull Definition definition;
-
- public CAEEEnchantment(@NotNull CustomEnchantment enchantment) {
- super(enchantment.getBukkitEnchantment(), EnchantmentRarity.getRarity(enchantment.getDefinition().getAnvilCost()));
- this.eeenchantment = enchantment;
- this.definition = enchantment.getDefinition();
-
- }
-
- @Override
- public boolean isEnchantConflict(@NotNull Map enchantments, @NotNull Material itemMat) {
- if(!definition.hasConflicts()) return false;
-
- Set conflicts = definition.getConflicts();
-
- for (CAEnchantment caEnchantment : enchantments.keySet()) {
- if(conflicts.contains(caEnchantment.getName())) return true;
- }
-
- return false;
- }
-
- @Override
- public boolean isItemConflict(@NotNull Map enchantments, @NotNull Material itemMat, @NotNull ItemStack item) {
- if(Material.ENCHANTED_BOOK.equals(itemMat)) return false;
-
- return !definition.getSupportedItems().is(item);
- }
-}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEEPreV5Enchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEEPreV5Enchantment.java
new file mode 100644
index 0000000..783798d
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEEPreV5Enchantment.java
@@ -0,0 +1,61 @@
+package xyz.alexcrea.cuanvil.enchant.wrapped;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import su.nightexpress.excellentenchants.api.enchantment.CustomEnchantment;
+import su.nightexpress.excellentenchants.api.enchantment.Definition;
+import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
+import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+
+public class CAEEPreV5Enchantment extends CABukkitEnchantment implements AdditionalTestEnchantment {
+
+ @NotNull CustomEnchantment eeenchantment;
+ @NotNull Definition definition;
+
+ public CAEEPreV5Enchantment(@NotNull CustomEnchantment enchantment) {
+ super(enchantment.getBukkitEnchantment(), getRarity(enchantment.getBukkitEnchantment()));
+ this.eeenchantment = enchantment;
+ try {
+ this.definition = (Definition) getDefinition.invoke(enchantment);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ private final static Method getDefinition;
+ static {
+ try {
+ getDefinition = CustomEnchantment.class.getMethod("getDefinition");
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public boolean isEnchantConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemType) {
+ if (!definition.hasConflicts()) return false;
+
+ Set conflicts = definition.getConflicts();
+
+ for (CAEnchantment caEnchantment : enchantments.keySet()) {
+ if (conflicts.contains(caEnchantment.getName())) return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isItemConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemType, @NotNull ItemStack item) {
+ if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) return false;
+
+ return !definition.getSupportedItems().is(item);
+ }
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEEV5Enchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEEV5Enchantment.java
new file mode 100644
index 0000000..2d8f945
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEEV5Enchantment.java
@@ -0,0 +1,128 @@
+package xyz.alexcrea.cuanvil.enchant.wrapped;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import su.nightexpress.excellentenchants.api.enchantment.CustomEnchantment;
+import su.nightexpress.excellentenchants.api.item.ItemSet;
+import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
+import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
+import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+
+public class CAEEV5Enchantment extends CABukkitEnchantment implements AdditionalTestEnchantment {
+
+ @NotNull CustomEnchantment eeenchantment;
+ @NotNull Object definition;
+
+ public CAEEV5Enchantment(@NotNull CustomEnchantment enchantment) {
+ super(enchantment.getBukkitEnchantment(), EnchantmentRarity.getRarity(getAnvilCost(enchantment)));
+ this.eeenchantment = enchantment;
+ this.definition = getDefinition(enchantment);
+
+ }
+
+ @Override
+ public boolean isEnchantConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemType) {
+ if (!hasConflicts()) return false;
+
+ Set conflicts = getExclusiveSet();
+
+ for (CAEnchantment caEnchantment : enchantments.keySet()) {
+ if (conflicts.contains(caEnchantment.getName())) return true;
+ if (conflicts.contains(caEnchantment.getKey().toString())) return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isItemConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemType, @NotNull ItemStack item) {
+ if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) return false;
+
+ String key = itemType.getKey();
+ ItemSet primary = eeenchantment.getPrimaryItems();
+ if (primary.getMaterials().contains(key)) return false;
+
+ ItemSet supported = eeenchantment.getSupportedItems();
+ if (supported.getMaterials().contains(key)) return false;
+
+ return true;
+ }
+
+
+ private static final Method getDefinitonMethod;
+
+ private static final Method getAnvilCostMethod;
+ private static final Method hasConflictsMethod;
+ private static final Method getExclusiveSetMethod;
+ static {
+ var enchClazz = CustomEnchantment.class;
+ try {
+ getDefinitonMethod = enchClazz.getDeclaredMethod("getDefinition");
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+
+ Class> definitionClazz;
+ try {
+ definitionClazz = Class.forName("su.nightexpress.excellentenchants.api.EnchantDefinition");
+ } catch (ClassNotFoundException e) {
+ try {
+ definitionClazz = Class.forName("su.nightexpress.excellentenchants.api.wrapper.EnchantDefinition");
+ } catch (ClassNotFoundException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ // Now definition methods
+ try {
+ getAnvilCostMethod = definitionClazz.getDeclaredMethod("getAnvilCost");
+ hasConflictsMethod = definitionClazz.getDeclaredMethod("hasConflicts");
+ getExclusiveSetMethod = definitionClazz.getDeclaredMethod("getExclusiveSet");
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ private static Object getDefinition(CustomEnchantment enchantment) {
+ try {
+ return getDefinitonMethod.invoke(enchantment);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static int getAnvilCost(CustomEnchantment enchantment) {
+ try {
+ return (int) getAnvilCostMethod.invoke(getDefinition(enchantment));
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private boolean hasConflicts() {
+ try {
+ return (boolean) hasConflictsMethod.invoke(definition);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ private Set getExclusiveSet() {
+ try {
+ return (Set) getExclusiveSetMethod.invoke(definition);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEEV5_4Enchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEEV5_4Enchantment.java
new file mode 100644
index 0000000..7fb8627
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEEV5_4Enchantment.java
@@ -0,0 +1,29 @@
+package xyz.alexcrea.cuanvil.enchant.wrapped;
+
+import org.bukkit.NamespacedKey;
+import org.jetbrains.annotations.NotNull;
+import su.nightexpress.excellentenchants.api.enchantment.CustomEnchantment;
+import xyz.alexcrea.cuanvil.dependency.plugins.ExcellentEnchant5_4EnchantSettings;
+import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
+
+import java.util.Map;
+
+public class CAEEV5_4Enchantment extends CAEEV5Enchantment {
+
+ public CAEEV5_4Enchantment(@NotNull CustomEnchantment enchantment) {
+ super(enchantment);
+ }
+
+ @Override
+ public boolean isEnchantConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemMat) {
+ if(super.isEnchantConflict(enchantments, itemMat)) return true;
+
+ var limit = ExcellentEnchant5_4EnchantSettings.anvilLimit();
+ var count = enchantments.keySet().stream()
+ .filter(key -> key instanceof CAEEV5_4Enchantment)
+ .count();
+
+ return count > limit;
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEcoEnchant.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEcoEnchant.java
index 0d85ffd..32d1346 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEcoEnchant.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEcoEnchant.java
@@ -2,13 +2,16 @@ package xyz.alexcrea.cuanvil.enchant.wrapped;
import com.willfp.ecoenchants.enchant.EcoEnchant;
import com.willfp.ecoenchants.target.EnchantmentTarget;
+import com.willfp.ecoenchants.type.EnchantmentType;
import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
+import java.util.HashMap;
import java.util.Map;
public class CAEcoEnchant extends CABukkitEnchantment implements AdditionalTestEnchantment {
@@ -21,33 +24,52 @@ public class CAEcoEnchant extends CABukkitEnchantment implements AdditionalTestE
}
@Override
- public boolean isEnchantConflict(@NotNull Map enchantments, @NotNull Material itemMat) {
- if(!enchantments.isEmpty()) {
- if (this.ecoEnchant.getConflictsWithEverything()) {
+ public boolean isEnchantConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemType) {
+ if (enchantments.isEmpty()) return false;
+
+ // Check if there is only self
+ if (enchantments.size() == 1 && this.equals(enchantments.keySet().stream().findFirst().get()))
+ return false;
+
+ if (this.ecoEnchant.getConflictsWithEverything()) {
+ return true;
+ }
+
+ HashMap typeAmountMap = new HashMap<>();
+
+ for (CAEnchantment other : enchantments.keySet()) {
+ if (other instanceof CABukkitEnchantment otherVanilla
+ && this.ecoEnchant.conflictsWith(otherVanilla.getEnchant())) {
return true;
}
- for (CAEnchantment other : enchantments.keySet()) {
- if(other instanceof CABukkitEnchantment otherVanilla
- && this.ecoEnchant.conflictsWith(otherVanilla.getEnchant())){
+ if (other instanceof CAEcoEnchant ecoOther) {
+ EnchantmentType type = ecoOther.ecoEnchant.getType();
+ typeAmountMap.putIfAbsent(type, 0);
+
+ int amount = typeAmountMap.get(type) + 1;
+ if (amount > type.getLimit()) {
return true;
}
+
+ typeAmountMap.put(type, amount);
}
}
+
return false;
}
@Override
public boolean isItemConflict(@NotNull Map enchantments,
- @NotNull Material itemMat,
+ @NotNull NamespacedKey itemType,
@NotNull ItemStack item) {
- if(Material.ENCHANTED_BOOK.equals(itemMat)){
+ if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) {
return false;
}
for (EnchantmentTarget target : this.ecoEnchant.getTargets()) {
- if(target.matches(item)){
+ if (target.matches(item)) {
return false;
}
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEnchantSquaredEnchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEnchantSquaredEnchantment.java
index ee5b9c0..8f1058e 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEnchantSquaredEnchantment.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAEnchantSquaredEnchantment.java
@@ -16,15 +16,20 @@ import java.util.Objects;
public class CAEnchantSquaredEnchantment extends CAEnchantmentBase {
public final @NotNull CustomEnchant enchant;
+
public CAEnchantSquaredEnchantment(@NotNull CustomEnchant enchant) {
super(Objects.requireNonNull(
- Objects.requireNonNull(DependencyManager.INSTANCE.getEnchantmentSquaredCompatibility()).getKeyFromEnchant(enchant)),
+ Objects.requireNonNull(DependencyManager.INSTANCE.getEnchantmentSquaredCompatibility()).getKeyFromEnchant(enchant)),
EnchantmentRarity.COMMON,
enchant.getMaxLevel());
this.enchant = enchant;
}
+ public @NotNull CustomEnchant getEnchant() {
+ return enchant;
+ }
+
@Override
public boolean isGetOptimised() {
return true;
@@ -61,4 +66,14 @@ public class CAEnchantSquaredEnchantment extends CAEnchantmentBase {
CustomEnchantManager.getInstance().removeEnchant(item, this.enchant.getType());
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CAEnchantSquaredEnchantment other)) {
+ return false;
+ }
+
+ return this.enchant.equals(other.getEnchant());
+ }
+
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAIncompatibleAllEnchant.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAIncompatibleAllEnchant.java
new file mode 100644
index 0000000..552ecd4
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CAIncompatibleAllEnchant.java
@@ -0,0 +1,36 @@
+package xyz.alexcrea.cuanvil.enchant.wrapped;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import xyz.alexcrea.cuanvil.enchant.*;
+
+import java.util.Map;
+
+/**
+ * Represent an enchantment incompatible with every other enchantments
+ */
+public class CAIncompatibleAllEnchant extends CABukkitEnchantment implements AdditionalTestEnchantment {
+
+ public CAIncompatibleAllEnchant(@NotNull Enchantment enchantment, @Nullable EnchantmentRarity rarity) {
+ super(enchantment, rarity);
+ }
+
+ public CAIncompatibleAllEnchant(@NotNull Enchantment enchantment) {
+ super(enchantment);
+ }
+
+
+ @Override
+ public boolean isEnchantConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemType) {
+ return !enchantments.isEmpty() && !(enchantments.size() == 1 && enchantments.containsKey(this));
+ }
+
+ @Override
+ public boolean isItemConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemType, @NotNull ItemStack item) {
+ return false;
+ }
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CALegacyEEEnchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CALegacyEEEnchantment.java
new file mode 100644
index 0000000..74068d4
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CALegacyEEEnchantment.java
@@ -0,0 +1,44 @@
+package xyz.alexcrea.cuanvil.enchant.wrapped;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import su.nightexpress.excellentenchants.api.enchantment.EnchantmentData;
+import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
+import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
+import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
+
+import java.util.Map;
+import java.util.Set;
+
+public class CALegacyEEEnchantment extends CABukkitEnchantment implements AdditionalTestEnchantment {
+
+ @NotNull EnchantmentData eeenchantment;
+
+ public CALegacyEEEnchantment(@NotNull EnchantmentData enchantment) {
+ super(enchantment.getEnchantment(), EnchantmentRarity.getRarity(enchantment.getAnvilCost()));
+ this.eeenchantment = enchantment;
+
+ }
+
+ @Override
+ public boolean isEnchantConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemType) {
+ if (!eeenchantment.hasConflicts()) return false;
+
+ Set conflicts = eeenchantment.getConflicts();
+
+ for (CAEnchantment caEnchantment : enchantments.keySet()) {
+ if (conflicts.contains(caEnchantment.getName())) return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isItemConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemType, @NotNull ItemStack item) {
+ if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) return false;
+
+ return !eeenchantment.getSupportedItems().is(item);
+ }
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CALegacyEcoEnchant.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CALegacyEcoEnchant.java
new file mode 100644
index 0000000..cb24def
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CALegacyEcoEnchant.java
@@ -0,0 +1,68 @@
+package xyz.alexcrea.cuanvil.enchant.wrapped;
+
+import com.willfp.ecoenchants.enchantments.EcoEnchant;
+import com.willfp.ecoenchants.enchantments.meta.EnchantmentTarget;
+import com.willfp.ecoenchants.enchantments.meta.EnchantmentType;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
+import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
+import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
+import xyz.alexcrea.cuanvil.util.MaterialUtil;
+
+import java.util.Map;
+
+public class CALegacyEcoEnchant extends CABukkitEnchantment implements AdditionalTestEnchantment {
+
+ private final @NotNull EcoEnchant ecoEnchant;
+
+ public CALegacyEcoEnchant(@NotNull EcoEnchant ecoEnchant, @NotNull Enchantment enchantment) {
+ super(enchantment, EnchantmentRarity.COMMON);
+ this.ecoEnchant = ecoEnchant;
+ }
+
+ @Override
+ public boolean isEnchantConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemType) {
+ if (enchantments.isEmpty()) return false;
+
+ EnchantmentType type = this.ecoEnchant.getType();
+ boolean isSingular = type.isSingular();
+
+ for (CAEnchantment other : enchantments.keySet()) {
+ if (other instanceof CABukkitEnchantment otherVanilla
+ && this.ecoEnchant.conflictsWith(otherVanilla.getEnchant())) {
+ return true;
+ }
+
+ if (isSingular &&
+ other != this &&
+ (other instanceof CALegacyEcoEnchant otherEco) &&
+ type.equals(otherEco.ecoEnchant.getType())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isItemConflict(@NotNull Map enchantments,
+ @NotNull NamespacedKey itemType,
+ @NotNull ItemStack item) {
+ if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) {
+ return false;
+ }
+
+ var mat = MaterialUtil.INSTANCE.getMatFromKey(itemType);
+ for (EnchantmentTarget target : this.ecoEnchant.getTargets()) {
+ if (target.getMaterials().contains(mat)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CASuperEnchantEnchantment.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CASuperEnchantEnchantment.java
new file mode 100644
index 0000000..6039dc8
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/wrapped/CASuperEnchantEnchantment.java
@@ -0,0 +1,76 @@
+package xyz.alexcrea.cuanvil.enchant.wrapped;
+
+import com.maddoxh.superEnchants.enchants.CustomEnchant;
+import com.maddoxh.superEnchants.enchants.EnchantManager;
+import com.maddoxh.superEnchants.items.EnchantApplicator;
+import com.maddoxh.superEnchants.items.EnchantReader;
+import com.maddoxh.superEnchants.util.ConflictChecker;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
+import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
+import xyz.alexcrea.cuanvil.enchant.CAEnchantmentBase;
+import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class CASuperEnchantEnchantment extends CAEnchantmentBase implements AdditionalTestEnchantment {
+
+ private @NotNull CustomEnchant enchant;
+ private @NotNull EnchantManager enchantManager;
+
+ public CASuperEnchantEnchantment(@NotNull CustomEnchant enchant, @NotNull Plugin plugin, @NotNull EnchantManager enchantManager) {
+ super(NamespacedKey.fromString(enchant.getId(), plugin), EnchantmentRarity.COMMON, enchant.getMaxLevel());
+
+ this.enchant = enchant;
+ this.enchantManager = enchantManager;
+ }
+
+ @Override
+ public int getLevel(@NotNull ItemStack item, @NotNull ItemMeta meta) {
+ return EnchantReader.INSTANCE.getEnchantLevel(item, enchant.getId());
+ }
+
+ @Override
+ public boolean isEnchantmentPresent(@NotNull ItemStack item, @NotNull ItemMeta meta) {
+ return EnchantReader.INSTANCE.hasEnchant(item, enchant.getId());
+ }
+
+ @Override
+ public void addEnchantmentUnsafe(@NotNull ItemStack item, int level) {
+ EnchantApplicator.INSTANCE.applyEnchant(item, enchant.getId(), level);
+ }
+
+ @Override
+ public void removeFrom(@NotNull ItemStack item) {
+ EnchantApplicator.INSTANCE.removeEnchant(item, enchant.getId());
+ }
+
+ @Override
+ public boolean isEnchantConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemType) {
+ var idMap = new HashMap();
+
+ enchantments.forEach((enchant, level) -> {
+ if(!(enchant instanceof CASuperEnchantEnchantment superEnch)) return;
+ idMap.put(superEnch.enchant.getId(), level);
+ });
+
+ return ConflictChecker.INSTANCE.hasConflict(
+ idMap,
+ enchant.getId(),
+ enchantManager
+ ) != null;
+ }
+
+ @Override
+ public boolean isItemConflict(@NotNull Map enchantments, @NotNull NamespacedKey itemType, @NotNull ItemStack item) {
+ if(Material.ENCHANTED_BOOK.equals(item.getType())) return false;
+
+ return !enchant.canApplyTo(item.getType());
+ }
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/MainConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/MainConfigGui.java
index e2224f7..cc4fddc 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/MainConfigGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/MainConfigGui.java
@@ -103,8 +103,8 @@ public class MainConfigGui extends ChestGui {
ItemMeta groupMeta = groupItemstack.getItemMeta();
assert groupMeta != null;
- groupMeta.setDisplayName("§aGroups");
- groupMeta.setLore(Collections.singletonList("§7Click here to open material group menu"));
+ groupMeta.setDisplayName("§aItem Groups");
+ groupMeta.setLore(Collections.singletonList("§7Click here to open item group menu"));
groupItemstack.setItemMeta(groupMeta);
GuiItem groupConfigItem = GuiGlobalItems.goToGuiItem(groupItemstack, GroupConfigGui.getInstance());
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/SelectMaterialContainer.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/SelectMaterialContainer.java
index 2f76694..3756341 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/SelectMaterialContainer.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/SelectMaterialContainer.java
@@ -1,34 +1,35 @@
package xyz.alexcrea.cuanvil.gui.config;
import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
import java.util.*;
public interface SelectMaterialContainer {
- EnumSet getSelectedMaterials();
+ Set getSelectedMaterials();
- boolean setSelectedMaterials(EnumSet materials);
+ boolean setSelectedMaterials(Set materials);
- EnumSet illegalMaterials();
+ Set illegalMaterials();
static List getMaterialLore(SelectMaterialContainer container, String containerType, String action){
// Prepare material lore
ArrayList groupLore = new ArrayList<>();
groupLore.add("§7Allow you to select a list of §ematerials §7that this " + containerType + " should " + action);
- Set materialSet = container.getSelectedMaterials();
+ Set materialSet = container.getSelectedMaterials();
if (materialSet.isEmpty()) {
groupLore.add("§7There is no "+action+"d material for this "+containerType+".");
} else {
groupLore.add("§7List of "+action+"d materials for this "+containerType+":");
- Iterator materialIterator = materialSet.iterator();
+ Iterator materialIterator = materialSet.iterator();
boolean greaterThanMax = materialSet.size() > 5;
int maxindex = (greaterThanMax ? 4 : materialSet.size());
for (int i = 0; i < maxindex; i++) {
// format string like "- Stone Sword"
- String formattedName = CasedStringUtil.snakeToUpperSpacedCase(materialIterator.next().name().toLowerCase());
+ String formattedName = CasedStringUtil.snakeToUpperSpacedCase(materialIterator.next().getKey().toLowerCase());
groupLore.add("§7- §e" + formattedName);
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/ask/ConfirmActionGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/ask/ConfirmActionGui.java
index 56bf848..5839663 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/ask/ConfirmActionGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/ask/ConfirmActionGui.java
@@ -11,6 +11,7 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
+import xyz.alexcrea.cuanvil.util.MetricsUtil;
import java.util.Arrays;
import java.util.function.Supplier;
@@ -41,6 +42,7 @@ public class ConfirmActionGui extends AbstractAskGui {
success = onConfirm.get();
} catch (Exception e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "Could not process confirmation supplier.", e);
+ MetricsUtil.INSTANCE.trackError(e);
success = false;
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/ask/SelectItemTypeGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/ask/SelectItemTypeGui.java
index b2d6afe..66411bd 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/ask/SelectItemTypeGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/ask/SelectItemTypeGui.java
@@ -12,6 +12,7 @@ import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
+import xyz.alexcrea.cuanvil.util.MaterialUtil;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
@@ -52,7 +53,7 @@ public class SelectItemTypeGui extends AbstractAskGui {
event.setCancelled(true);
ItemStack cursor = event.getWhoClicked().getItemOnCursor();
- if(cursor.getType().isAir()) return;
+ if(MaterialUtil.INSTANCE.isAir(cursor)) return;
ItemStack finalItem;
if(materialOnly){
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/AbstractEnchantConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/AbstractEnchantConfigGui.java
index a65b54b..6bd7ea3 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/AbstractEnchantConfigGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/AbstractEnchantConfigGui.java
@@ -20,7 +20,7 @@ import java.util.function.Consumer;
*
* @param Type of the factory of the type of setting the gui should edit.
*/
-public abstract class AbstractEnchantConfigGui extends SettingGuiListConfigGui implements ValueUpdatableGui {
+public abstract class AbstractEnchantConfigGui extends SettingGuiListConfigGui{
/**
* Constructor for a gui displaying available enchantment to edit a enchantment setting.
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/BasicConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/BasicConfigGui.java
index d689921..51936c7 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/BasicConfigGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/BasicConfigGui.java
@@ -14,13 +14,13 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
-import xyz.alexcrea.cuanvil.config.WorkPenaltyType;
+import xyz.alexcrea.cuanvil.dependency.MinecraftVersionUtil;
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager;
import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui;
import xyz.alexcrea.cuanvil.gui.config.settings.BoolSettingsGui;
-import xyz.alexcrea.cuanvil.gui.config.settings.EnumSettingGui;
import xyz.alexcrea.cuanvil.gui.config.settings.IntSettingsGui;
+import xyz.alexcrea.cuanvil.gui.config.settings.WorkPenaltyTypeSettingGui;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
@@ -28,7 +28,6 @@ import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.List;
/**
* Global config to edit basic basic settings.
@@ -89,8 +88,6 @@ public class BasicConfigGui extends ChestGui implements ValueUpdatableGui {
private IntSettingsGui.IntSettingFactory itemRenameCost; // r character
private IntSettingsGui.IntSettingFactory sacrificeIllegalEnchantCost; // S character
- private EnumSettingGui.EnumSettingFactory workPenaltyType; // W character
-
private BoolSettingsGui.BoolSettingFactory allowColorCode; // c character
private BoolSettingsGui.BoolSettingFactory allowHexColor; // h character
@@ -211,46 +208,6 @@ public class BasicConfigGui extends ChestGui implements ValueUpdatableGui {
ConfigOptions.DEFAULT_SACRIFICE_ILLEGAL_COST,
1, 5, 10, 50, 100);
- // -------------
- // Work Penalty
- // -------------
-
- this.workPenaltyType = new EnumSettingGui.EnumSettingFactory<>("§8Work Penalty Type", this,
- ConfigOptions.WORK_PENALTY_TYPE, ConfigHolder.DEFAULT_CONFIG
- ) {
- @NotNull
- @Override
- public WorkPenaltyType getConfiguredValue() {
- return ConfigOptions.INSTANCE.getWorkPenaltyType();
- }
-
- @NotNull
- @Override
- public WorkPenaltyType getDefault() {
- return WorkPenaltyType.DEFAULT;
- }
-
- @NotNull
- @Override
- public List getDisplayLore(WorkPenaltyType value) {
- return List.of(
- "§7Work penalty increase the price for every anvil use.",
- "§7This config allow you to choose the comportment of work penalty.",
- "",
- value.configDisplayForAdd(),
- value.configDisplayForIncrease()
-
- );
- }
-
- @NotNull
- @Override
- public WorkPenaltyType next(@NotNull WorkPenaltyType now) {
- return WorkPenaltyType.next(now);
- }
-
- };
-
// -------------
// Color config
// -------------
@@ -327,7 +284,7 @@ public class BasicConfigGui extends ChestGui implements ValueUpdatableGui {
if(!this.packetManager.getCanSetInstantBuild()){
lore.add("");
- lore.add("§4/!\\§cCaution§4/!\\ §cYou need ProtocoLib installed and working or a newer version of this plugin for this to work.");
+ lore.add("§4/!\\§cCaution§4/!\\ §cYou need ProtocoLib installed and working or a paper server.");
lore.add("§cCurrently ProtocoLib is not detected.");
}
@@ -377,7 +334,7 @@ public class BasicConfigGui extends ChestGui implements ValueUpdatableGui {
pane.bindItem('S', illegalCostItem);
// work penalty type
- GuiItem workPenaltyType = this.workPenaltyType.getItem(Material.DAMAGED_ANVIL, "§aWork Penalty Type");
+ GuiItem workPenaltyType = WorkPenaltyTypeSettingGui.getDisplayItem(this, Material.DAMAGED_ANVIL, "§aWork Penalty Type");
pane.bindItem('W', workPenaltyType);
// allow color code
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/CustomRecipeConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/CustomRecipeConfigGui.java
index 5a68e6e..e21ad75 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/CustomRecipeConfigGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/CustomRecipeConfigGui.java
@@ -14,22 +14,22 @@ import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collection;
-public class CustomRecipeConfigGui extends MappedGuiListConfigGui {
-
+public class CustomRecipeConfigGui extends MappedGuiListConfigGui> {
private static CustomRecipeConfigGui INSTANCE = new CustomRecipeConfigGui();
@Nullable
- public static CustomRecipeConfigGui getCurrentInstance(){
+ public static CustomRecipeConfigGui getCurrentInstance() {
return INSTANCE;
}
@NotNull
- public static CustomRecipeConfigGui getInstance(){
- if(INSTANCE == null) INSTANCE = new CustomRecipeConfigGui();
+ public static CustomRecipeConfigGui getInstance() {
+ if (INSTANCE == null) INSTANCE = new CustomRecipeConfigGui();
return INSTANCE;
}
@@ -44,36 +44,43 @@ public class CustomRecipeConfigGui extends MappedGuiListConfigGui getRecipeLore(AnvilCustomRecipe recipe) {
boolean shouldWork = recipe.validate();
- meta.setLore(Arrays.asList(
- "§7Should work: §"+(shouldWork ? "aYes" : "cNo"),
- "§7Exact count: §"+(recipe.getExactCount() ? "aYes" : "cNo"),
- "§7Recipe Xp Cost: §e"+recipe.getXpCostPerCraft()
-
- ));
-
- displaydItem.setItemMeta(meta);
- return displaydItem;
+ ArrayList lore = new ArrayList<>();
+ lore.add("§7Is valid: §" + (shouldWork ? "aYes" : "cNo"));
+ lore.add("§7Exact count: §" + (recipe.getExactCount() ? "aYes" : "cNo"));
+ lore.add("§7Recipe Level Cost: §e" + recipe.getLevelCostPerCraft());
+ lore.add("§7Recipe Linear Xp Cost: §e" + recipe.getXpCostPerCraft());
+ if (recipe.getXpCostPerCraft() != 0) {
+ lore.add("§7Exact Linear xp remove: §" + (recipe.getRemoveExactLinearXp() ? "aYes" : "cNo"));
+ }
+ return lore;
}
@Override
- protected CustomRecipeSubSettingGui newInstanceOfGui(AnvilCustomRecipe generic, GuiItem item) {
- return new CustomRecipeSubSettingGui(this, generic, item);
+ protected LazyElement newInstanceOfGui(AnvilCustomRecipe generic, GuiItem item) {
+ return new LazyElement<>(item, () -> new CustomRecipeSubSettingGui(this, generic));
}
@Override
@@ -87,7 +94,11 @@ public class CustomRecipeConfigGui extends MappedGuiListConfigGui {
+public class EnchantConflictGui extends MappedGuiListConfigGui> {
private static EnchantConflictGui INSTANCE;
@@ -86,8 +87,8 @@ public class EnchantConflictGui extends MappedGuiListConfigGui newInstanceOfGui(EnchantConflictGroup conflict, GuiItem item) {
+ return new LazyElement<>(item, () -> new EnchantConflictSubSettingGui(this, conflict));
}
@Override
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantLimitConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantLimitConfigGui.java
index d624bff..e9edbeb 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantLimitConfigGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantLimitConfigGui.java
@@ -17,7 +17,7 @@ import java.util.Locale;
*/
public class EnchantLimitConfigGui extends AbstractEnchantConfigGui {
- private static final String SECTION_NAME = "enchant_limits";
+ private static final String SECTION_NAME = ConfigOptions.ENCHANT_LIMIT_ROOT;
private static EnchantLimitConfigGui INSTANCE = null;
@@ -41,18 +41,34 @@ public class EnchantLimitConfigGui extends AbstractEnchantConfigGui "Default (" + defaultValue + ")";
+ case RESET -> String.valueOf(defaultValue);
+ default -> "Default";
+ };
+
+ }
+ else return super.valueDisplayName(type, value);
}
};
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/GroupConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/GroupConfigGui.java
index 8684d4d..8e20751 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/GroupConfigGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/GroupConfigGui.java
@@ -15,12 +15,13 @@ import xyz.alexcrea.cuanvil.group.ItemGroupManager;
import xyz.alexcrea.cuanvil.gui.config.list.MappedGuiListConfigGui;
import xyz.alexcrea.cuanvil.gui.config.list.elements.GroupConfigSubSettingGui;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
+import xyz.alexcrea.cuanvil.util.LazyValue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-public class GroupConfigGui extends MappedGuiListConfigGui {
+public class GroupConfigGui extends MappedGuiListConfigGui> {
private static GroupConfigGui INSTANCE;
@@ -73,8 +74,8 @@ public class GroupConfigGui extends MappedGuiListConfigGui newInstanceOfGui(IncludeGroup group, GuiItem item) {
+ return new LazyElement<>(item, () -> new GroupConfigSubSettingGui(this, group));
}
@Override
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/UnitRepairConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/UnitRepairConfigGui.java
index 833aea1..0e366ae 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/UnitRepairConfigGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/UnitRepairConfigGui.java
@@ -18,7 +18,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-public class UnitRepairConfigGui extends MappedGuiListConfigGui {
+public class UnitRepairConfigGui extends
+ MappedGuiListConfigGui> {
private static UnitRepairConfigGui INSTANCE;
@@ -41,10 +42,12 @@ public class UnitRepairConfigGui extends MappedGuiListConfigGui newInstanceOfGui(Material material, GuiItem item) {
+ return new LazyElement<>(item, () -> {
+ UnitRepairElementListGui element = new UnitRepairElementListGui(material, this);
+ element.init();
+ return element;
+ });
}
@Override
@@ -115,7 +118,7 @@ public class UnitRepairConfigGui extends MappedGuiListConfigGui getInstanceOrCreate(Material mat){
+ LazyElement element = this.elementGuiMap.get(mat);
if(element == null){
updateValueForGeneric(mat, false);
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/MappedGuiListConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/MappedGuiListConfigGui.java
index cf4b852..3aa18e0 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/MappedGuiListConfigGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/MappedGuiListConfigGui.java
@@ -3,15 +3,19 @@ package xyz.alexcrea.cuanvil.gui.config.list;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import io.delilaheve.CustomAnvil;
import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryClickEvent;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.gui.config.list.elements.ElementMappedToListGui;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
+import xyz.alexcrea.cuanvil.util.LazyValue;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
+import java.util.function.Supplier;
-public abstract class MappedGuiListConfigGui< T, S extends ElementMappedToListGui> extends MappedElementListConfigGui< T, S > {
+public abstract class MappedGuiListConfigGui< T, S extends MappedGuiListConfigGui.LazyElement>>
+ extends MappedElementListConfigGui< T, S > {
protected MappedGuiListConfigGui(@NotNull String title) {
super(title);
@@ -20,7 +24,10 @@ public abstract class MappedGuiListConfigGui< T, S extends ElementMappedToListGu
@Override
public void reloadValues() {
- this.elementGuiMap.forEach((conflict, gui) -> gui.cleanAndBeUnusable());
+ this.elementGuiMap.forEach((conflict, element) -> {
+ ElementMappedToListGui gui = element.getStored();
+ if(gui != null) gui.cleanAndBeUnusable();
+ });
this.elementGuiMap.clear();
super.reloadValues();
@@ -30,23 +37,24 @@ public abstract class MappedGuiListConfigGui< T, S extends ElementMappedToListGu
protected S newElementRequested(T generic, GuiItem newItem) {
S element = newInstanceOfGui(generic, newItem);
- newItem.setAction(GuiGlobalActions.openGuiAction(element.getMappedGui()));
+ newItem.setAction(element.openAction());
return element;
}
@Override
protected GuiItem findItemFromElement(T generic, S element) {
- return element.getParentItemForThisGui();
+ return element.getParentItem();
}
@Override
protected void updateElement(T generic, S element) {
- element.updateLocal();
+ ElementMappedToListGui gui = element.getStored();
+ if(gui != null) gui.updateLocal();
}
@Override
protected GuiItem findGuiItemForRemoval(T generic, S element) {
- return element.getParentItemForThisGui();
+ return element.getParentItem();
}
@Override
@@ -90,7 +98,7 @@ public abstract class MappedGuiListConfigGui< T, S extends ElementMappedToListGu
updateValueForGeneric(generic, true);
// show the new conflict config to the player
- this.elementGuiMap.get(generic).getMappedGui().show(player);
+ this.elementGuiMap.get(generic).get().getMappedGui().show(player);
update();
};
@@ -105,4 +113,28 @@ public abstract class MappedGuiListConfigGui< T, S extends ElementMappedToListGu
protected abstract T createAndSaveNewEmptyGeneric(String name);
+ public static class LazyElement extends LazyValue {
+
+ private final GuiItem parentItem;
+ private final LazyValue> lazyOpenConsumer;
+ public LazyElement(GuiItem parentItem, Supplier valueSupplier) {
+ super(valueSupplier);
+ this.parentItem = parentItem;
+
+ this.lazyOpenConsumer = new LazyValue<>(() ->
+ GuiGlobalActions.openGuiAction(this.get().getMappedGui()))
+ ;
+ }
+
+ public GuiItem getParentItem() {
+ return parentItem;
+ }
+
+ @NotNull
+ public Consumer openAction(){
+ return event -> lazyOpenConsumer.get().accept(event);
+ }
+
+ }
+
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/UnitRepairElementListGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/UnitRepairElementListGui.java
index 0bf2a03..35f8ebb 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/UnitRepairElementListGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/UnitRepairElementListGui.java
@@ -26,17 +26,14 @@ import java.util.function.Consumer;
public class UnitRepairElementListGui extends SettingGuiListConfigGui implements ElementMappedToListGui {
- private final GuiItem parentItem;
private final Material parentMaterial;
private final UnitRepairConfigGui parentGui;
private final String materialName;
private boolean shouldWork = true;
public UnitRepairElementListGui(@NotNull Material parentMaterial,
- @NotNull UnitRepairConfigGui parentGui,
- @NotNull GuiItem parentItem) {
+ @NotNull UnitRepairConfigGui parentGui) {
super("§e" + CasedStringUtil.snakeToUpperSpacedCase(parentMaterial.name().toLowerCase()) + " §rUnit repair");
- this.parentItem = parentItem;
this.parentMaterial = parentMaterial;
this.parentGui = parentGui;
this.materialName = CasedStringUtil.snakeToUpperSpacedCase(parentMaterial.name().toLowerCase());
@@ -165,11 +162,6 @@ public class UnitRepairElementListGui extends SettingGuiListConfigGui {
- event.setCancelled(true);
+ String materialSelectionName = "§e" + CasedStringUtil.snakeToUpperSpacedCase(group.getName()) + " §rMaterials";
+ ItemStack selectItem = new ItemStack(Material.DIAMOND_SWORD);
+ ItemMeta selectItemMeta = selectItem.getItemMeta();
+ selectItemMeta.setDisplayName(materialSelectionName);
+ selectItem.setItemMeta(selectItemMeta);
+ this.materialSelection = new GuiItem(selectItem, (event) -> {
+ event.setCancelled(true);
MaterialSelectSettingGui selectGui = new MaterialSelectSettingGui(this,
- CasedStringUtil.snakeToUpperSpacedCase(group.getName()) + " Materials"
+ materialSelectionName
, this);
selectGui.show(event.getWhoClicked());
}, CustomAnvil.instance);
- this.groupSelection = new GuiItem(new ItemStack(Material.CHEST), (event) -> {
+ String selectGroupName = "§e" + CasedStringUtil.snakeToUpperSpacedCase(this.group.getName()) + " §rGroups";
+ ItemStack selectGroup = new ItemStack(Material.CHEST);
+ ItemMeta selectGroupMeta = selectGroup.getItemMeta();
+ selectGroupMeta.setDisplayName(selectGroupName);
+
+ selectGroup.setItemMeta(selectGroupMeta);
+ this.groupSelection = new GuiItem(selectGroup, (event) -> {
event.setCancelled(true);
GroupSelectSettingGui enchantGui = new GroupSelectSettingGui(
- CasedStringUtil.snakeToUpperSpacedCase(this.group.getName()) + " Groups",
+ selectGroupName,
this, this, 0);
enchantGui.show(event.getWhoClicked());
}, CustomAnvil.instance);
@@ -311,23 +322,23 @@ public class GroupConfigSubSettingGui extends MappedToListSubSettingGui implemen
// ----------------------------
// End of SelectGroupContainer related methods
// ----------------------------
- // SelectGroupContainer related methods
+ // SelectMaterialContainer related methods
// ----------------------------
@Override
- public EnumSet getSelectedMaterials() {
+ public Set getSelectedMaterials() {
return this.group.getNonGroupInheritedMaterials();
}
@Override
- public boolean setSelectedMaterials(EnumSet materials) {
+ public boolean setSelectedMaterials(Set materials) {
this.group.setNonGroupInheritedMaterials(materials);
// Write to file configuration
String[] groupNames = new String[materials.size()];
int index = 0;
- for (Material otherGroup : materials) {
- groupNames[index++] = otherGroup.name().toLowerCase();
+ for (NamespacedKey otherGroup : materials) {
+ groupNames[index++] = otherGroup.getKey().toLowerCase();
}
ConfigHolder.ITEM_GROUP_HOLDER.getConfig().set(this.group.getName()+"."+ItemGroupManager.MATERIAL_LIST_PATH, groupNames);
@@ -343,12 +354,12 @@ public class GroupConfigSubSettingGui extends MappedToListSubSettingGui implemen
}
@Override
- public EnumSet illegalMaterials() {
- return EnumSet.of(Material.AIR);
+ public Set illegalMaterials() {
+ return Set.of(Material.AIR.getKey());
}
// ----------------------------
- // End of SelectGroupContainer related methods
+ // End of SelectMaterialContainer related methods
// ----------------------------
private void updateDirectReferencingGroups(AbstractMaterialGroup referenceTo){
@@ -371,7 +382,7 @@ public class GroupConfigSubSettingGui extends MappedToListSubSettingGui implemen
for (AbstractMaterialGroup otherGroup : everyStoredGroups) {
if(otherGroup.getGroups().contains(testGroup)){
otherGroup.updateMaterials();
- toUpdate.add(otherGroup);
+ updateFuture.add(otherGroup);
}
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/elements/MappedToListSubSettingGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/elements/MappedToListSubSettingGui.java
index 020e6ed..1d86781 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/elements/MappedToListSubSettingGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/list/elements/MappedToListSubSettingGui.java
@@ -9,18 +9,10 @@ import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
public abstract class MappedToListSubSettingGui extends ChestGui implements ValueUpdatableGui, ElementMappedToListGui {
- private final GuiItem item;
protected MappedToListSubSettingGui(
- GuiItem item,
int rows,
@NotNull String title) {
super(rows, title, CustomAnvil.instance);
- this.item = item;
- }
-
- @Override
- public GuiItem getParentItemForThisGui() {
- return item;
}
@Override
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/IntSettingsGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/IntSettingsGui.java
index af977e9..73121a6 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/IntSettingsGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/IntSettingsGui.java
@@ -72,7 +72,8 @@ public class IntSettingsGui extends AbstractSettingGui {
assert meta != null;
meta.setDisplayName("§eReset to default value");
- meta.setLore(Collections.singletonList("§7Default value is §e" + holder.defaultVal));
+ meta.setLore(Collections.singletonList("§7Default value is §e" +
+ holder.valueDisplayName(ValueDisplayType.RESET, holder.defaultVal)));
item.setItemMeta(meta);
returnToDefault = new GuiItem(item, event -> {
event.setCancelled(true);
@@ -86,41 +87,23 @@ public class IntSettingsGui extends AbstractSettingGui {
* Update item using the setting value to match the new value.
*/
protected void updateValueDisplay() {
-
PatternPane pane = getPane();
// minus item
GuiItem minusItem;
if (now > holder.min) {
int planned = Math.max(holder.min, now - step);
- ItemStack item = new ItemStack(Material.RED_TERRACOTTA);
- ItemMeta meta = item.getItemMeta();
- assert meta != null;
-
- meta.setDisplayName("§e" + now + " §f-> §e" + planned + " §r(§c-" + (now - planned) + "§r)");
- meta.setLore(Collections.singletonList(AbstractSettingGui.CLICK_LORE));
- item.setItemMeta(meta);
-
- minusItem = new GuiItem(item, updateNowConsumer(planned), CustomAnvil.instance);
+ minusItem = valueEditItem(Material.RED_TERRACOTTA, ValueDisplayType.REMOVE, planned);
} else {
minusItem = GuiGlobalItems.backgroundItem(Material.BARRIER);
}
pane.bindItem('-', minusItem);
//plus item
- // may do a function to generalise ?
GuiItem plusItem;
if (now < holder.max) {
int planned = Math.min(holder.max, now + step);
- ItemStack item = new ItemStack(Material.GREEN_TERRACOTTA);
- ItemMeta meta = item.getItemMeta();
- assert meta != null;
-
- meta.setDisplayName("§e" + now + " §f-> §e" + planned + " §r(§a+" + (planned - now) + "§r)");
- meta.setLore(Collections.singletonList(AbstractSettingGui.CLICK_LORE));
- item.setItemMeta(meta);
-
- plusItem = new GuiItem(item, updateNowConsumer(planned), CustomAnvil.instance);
+ plusItem = valueEditItem(Material.GREEN_TERRACOTTA, ValueDisplayType.ADD, planned);
} else {
plusItem = GuiGlobalItems.backgroundItem(Material.BARRIER);
}
@@ -131,7 +114,7 @@ public class IntSettingsGui extends AbstractSettingGui {
ItemMeta resultMeta = resultPaper.getItemMeta();
assert resultMeta != null;
- resultMeta.setDisplayName("§fValue: §e" + now);
+ resultMeta.setDisplayName("§fValue: §e" + holder.valueDisplayName(ValueDisplayType.CURRENT, now));
resultMeta.setLore(holder.displayLore);
resultPaper.setItemMeta(resultMeta);
@@ -149,7 +132,21 @@ public class IntSettingsGui extends AbstractSettingGui {
}
pane.bindItem('D', returnToDefault);
+ }
+ private GuiItem valueEditItem(Material mat, ValueDisplayType type, int planned) {
+ ItemStack item = new ItemStack(mat);
+ ItemMeta meta = item.getItemMeta();
+ assert meta != null;
+
+ var nowDisplay = holder.valueDisplayName(type, now);
+ var plannedDisplay = holder.valueDisplayName(type, planned);
+ var deltaDisplay = holder.deltaDisplay(type, now, planned);
+ meta.setDisplayName("§e" + nowDisplay + " §f-> §e" + plannedDisplay + " §r(§c" + deltaDisplay + "§r)");
+
+ meta.setLore(Collections.singletonList(AbstractSettingGui.CLICK_LORE));
+ item.setItemMeta(meta);
+ return new GuiItem(item, updateNowConsumer(planned), CustomAnvil.instance);
}
/**
@@ -389,6 +386,23 @@ public class IntSettingsGui extends AbstractSettingGui {
return getItem(itemMat, CasedStringUtil.detectToUpperSpacedCase(configPath));
}
+ protected String valueDisplayName(ValueDisplayType type, int value) {
+ return String.valueOf(value);
+ }
+
+ protected String deltaDisplay(ValueDisplayType type, int now, int planned) {
+ var delta = planned - now;
+ if(delta < 0) return "§c" + delta;
+ else return "§a+" + delta;
+ }
+
+ }
+
+ public enum ValueDisplayType {
+ ADD,
+ CURRENT,
+ REMOVE,
+ RESET,
}
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/MaterialSelectSettingGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/MaterialSelectSettingGui.java
index a3963ce..fc519ff 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/MaterialSelectSettingGui.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/MaterialSelectSettingGui.java
@@ -5,6 +5,7 @@ import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
import com.github.stefvanschie.inventoryframework.pane.util.Pattern;
import io.delilaheve.CustomAnvil;
import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemFlag;
@@ -18,18 +19,19 @@ import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
+import xyz.alexcrea.cuanvil.util.MaterialUtil;
import java.util.*;
import java.util.function.Consumer;
-public class MaterialSelectSettingGui extends MappedElementListConfigGui {
+public class MaterialSelectSettingGui extends MappedElementListConfigGui {
private final SelectMaterialContainer selector;
private final Gui backGui;
private boolean instantRemove;
- private final List defaultMaterials;
- private final EnumSet illegalMaterials;
+ private final List defaultMaterials;
+ private final Set illegalMaterials;
private final int defaultMaterialHash;
private int nowMaterialHash;
@@ -161,8 +163,7 @@ public class MaterialSelectSettingGui extends MappedElementListConfigGui result = EnumSet.noneOf(Material.class);
- result.addAll(this.elementGuiMap.keySet());
+ Set result = new HashSet<>(this.elementGuiMap.keySet());
if(!this.selector.setSelectedMaterials(result)){
player.sendMessage("§cSomething went wrong while saving the change of value.");
@@ -185,8 +186,8 @@ public class MaterialSelectSettingGui extends MappedElementListConfigGui getEveryDisplayableInstanceOfGeneric() {
+ protected Collection getEveryDisplayableInstanceOfGeneric() {
return this.defaultMaterials;
}
@Override
- protected void updateElement(Material material, GuiItem element) {
+ protected void updateElement(NamespacedKey material, GuiItem element) {
// Nothing happen here I think
}
@Override
- protected GuiItem newElementRequested(Material material, GuiItem newItem) {
+ protected GuiItem newElementRequested(NamespacedKey material, GuiItem newItem) {
newItem.setAction(event -> {
if(this.instantRemove){
removeMaterial(material);
}else {
- String materialName = CasedStringUtil.snakeToUpperSpacedCase(material.name().toLowerCase());
+ String materialName = CasedStringUtil.snakeToUpperSpacedCase(material.getKey().toLowerCase());
// Create and show confirm remove gui.
ConfirmActionGui confirmGui = new ConfirmActionGui(
@@ -250,7 +251,7 @@ public class MaterialSelectSettingGui extends MappedElementListConfigGui materialList){
+ private static int hashFromMaterialList(List materialList){
int defaultMaterialHash = 0;
- for (Material material : materialList) {
+ for (NamespacedKey material : materialList) {
defaultMaterialHash ^= material.hashCode();
}
return defaultMaterialHash;
diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/WorkPenaltyTypeSettingGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/WorkPenaltyTypeSettingGui.java
new file mode 100644
index 0000000..4345aa1
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/WorkPenaltyTypeSettingGui.java
@@ -0,0 +1,252 @@
+package xyz.alexcrea.cuanvil.gui.config.settings;
+
+import com.github.stefvanschie.inventoryframework.gui.GuiItem;
+import com.github.stefvanschie.inventoryframework.pane.PatternPane;
+import com.github.stefvanschie.inventoryframework.pane.util.Pattern;
+import io.delilaheve.CustomAnvil;
+import io.delilaheve.util.ConfigOptions;
+import org.bukkit.Material;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.jetbrains.annotations.NotNull;
+import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
+import xyz.alexcrea.cuanvil.config.ConfigHolder;
+import xyz.alexcrea.cuanvil.config.WorkPenaltyType;
+import xyz.alexcrea.cuanvil.gui.config.global.BasicConfigGui;
+import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
+
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+
+public class WorkPenaltyTypeSettingGui extends AbstractSettingGui {
+
+ private static final String INCREASING_EXPLANATION = "§eIncreasing§7: will penalty be increased (in item)";
+ private static final String ADDING_EXPLANATION = "§eAdditive§7: will penalty be added to the cost";
+
+ private static final String SHARED_EXPLANATION = "§eShared§7: Vanilla, shared penalty. it will be kept from before the plugin installation.";
+ private static final String EXCLUSIVE_EXPLANATION = "§eExclusive§7: Custom, per anvil use type penalty. it will be lost after plugin uninstallation";
+
+ private final @NotNull WorkPenaltyType currentType;
+ private final @NotNull Map items;
+
+ public WorkPenaltyTypeSettingGui(@NotNull BasicConfigGui parent) {
+ super(4, "§8Work Penalty Type", parent);
+
+ this.currentType = ConfigOptions.INSTANCE.getWorkPenaltyType();
+ this.items = new EnumMap<>(this.currentType.getPartMap());
+
+ for (AnvilUseType type : useTypes.keySet()) {
+ updateGuiForType(type);
+ }
+ }
+
+ public static GuiItem getDisplayItem(@NotNull BasicConfigGui parent,
+ @NotNull Material itemMat,
+ @NotNull String name) {
+ List displayLore = new ArrayList<>();
+
+ displayLore.add("§7Work penalty increase the price for every anvil use.");
+ displayLore.add("§7This config allow you to choose the comportment of work penalty.");
+ displayLore.add(INCREASING_EXPLANATION);
+ displayLore.add(ADDING_EXPLANATION);
+ displayLore.add("");
+ displayLore.add("§7About shared/exclusive penalty:");
+ displayLore.add(SHARED_EXPLANATION);
+ displayLore.add(EXCLUSIVE_EXPLANATION);
+
+ ItemStack item = new ItemStack(itemMat);
+
+ ItemMeta meta = item.getItemMeta();
+ meta.setDisplayName(name);
+ meta.setLore(displayLore);
+
+ item.setItemMeta(meta);
+
+ return new GuiItem(item, (event) -> {
+ event.setCancelled(true);
+ HumanEntity player = event.getWhoClicked();
+
+ // Do not allow to open inventory if player do not have edit configuration permission
+ if (!player.hasPermission(CustomAnvil.editConfigPermission)) {
+ player.closeInventory();
+ player.sendMessage(GuiGlobalActions.NO_EDIT_PERM);
+ return;
+ }
+ new WorkPenaltyTypeSettingGui(parent).show(player);
+ }, CustomAnvil.instance);
+ }
+
+ private static final Map useTypes =
+ Map.of(
+ AnvilUseType.RENAME_ONLY, "a1z9Z",
+ AnvilUseType.MERGE, "b2y8Y",
+ AnvilUseType.UNIT_REPAIR, "c3x7X",
+ AnvilUseType.CUSTOM_CRAFT, "d4w6W"
+ );
+
+ @Override
+ protected Pattern getGuiPattern() {
+ return new Pattern( // Yeah that a mess
+ "00a1z9Z00",
+ "00b2y8Y00",
+ "00c3x7X00",
+ "B004w600S"
+ );
+ }
+
+ public void updateGuiForType(AnvilUseType type) {
+ PatternPane pane = getPane();
+
+ String typeVals = useTypes.get(type);
+
+ char increment = typeVals.charAt(0);
+ char additive = typeVals.charAt(1);
+ char display = typeVals.charAt(2);
+ char exclusiveIncrement = typeVals.charAt(3);
+ char exclusiveAdditive = typeVals.charAt(4);
+
+ WorkPenaltyType.WorkPenaltyPart part = items.get(type);
+ String increasingStr = (part.penaltyIncrease() ? "§a" : "§c") + "Increasing";
+ String additiveStr = (part.penaltyAdditive() ? "§a" : "§c") + "Additive";
+ String exclusiveIncreasingStr = (part.exclusivePenaltyIncrease() ? "§a" : "§c") + "Increasing";
+ String exclusiveAdditiveStr = (part.exclusivePenaltyAdditive() ? "§a" : "§c") + "Additive";
+
+ // Display item
+ ItemStack displayItem = new ItemStack(type.getDisplayMat());
+
+ ArrayList displayLore = new ArrayList<>();
+ displayLore.add("§eShared§7: " + additiveStr + " §7| " + increasingStr);
+ displayLore.add("§eExclusive§7: " + exclusiveAdditiveStr + " §7| " + exclusiveIncreasingStr);
+
+ ItemMeta meta = displayItem.getItemMeta();
+ meta.setDisplayName("§e" + type.getDisplayName());
+ meta.setLore(displayLore);
+ displayItem.setItemMeta(meta);
+
+ pane.bindItem(display, new GuiItem(displayItem, (event) -> {
+ event.setCancelled(true);
+ }));
+
+ // Can probably put this in a function but this works so
+ // "Increment" item
+ ItemStack incrementItem = new ItemStack(part.penaltyIncrease() ? Material.GREEN_TERRACOTTA : Material.RED_TERRACOTTA);
+
+ meta = incrementItem.getItemMeta();
+ meta.setDisplayName(increasingStr);
+ meta.setLore(List.of(INCREASING_EXPLANATION));
+ meta.setLore(List.of(SHARED_EXPLANATION));
+ incrementItem.setItemMeta(meta);
+
+ pane.bindItem(increment, new GuiItem(incrementItem, (event) -> {
+ event.setCancelled(true);
+
+ WorkPenaltyType.WorkPenaltyPart newPart = new WorkPenaltyType.WorkPenaltyPart(
+ !part.penaltyIncrease(), part.penaltyAdditive(),
+ part.exclusivePenaltyIncrease(), part.exclusivePenaltyAdditive());
+ items.replace(type, newPart);
+ updateGuiForType(type);
+ update();
+ }));
+
+ // "Additive" item
+ ItemStack additiveItem = new ItemStack(part.penaltyAdditive() ? Material.GREEN_TERRACOTTA : Material.RED_TERRACOTTA);
+
+ meta = additiveItem.getItemMeta();
+ meta.setDisplayName(additiveStr);
+ meta.setLore(List.of(ADDING_EXPLANATION));
+ meta.setLore(List.of(SHARED_EXPLANATION));
+ additiveItem.setItemMeta(meta);
+
+ pane.bindItem(additive, new GuiItem(additiveItem, (event) -> {
+ event.setCancelled(true);
+
+ WorkPenaltyType.WorkPenaltyPart newPart = new WorkPenaltyType.WorkPenaltyPart(
+ part.penaltyIncrease(), !part.penaltyAdditive(),
+ part.exclusivePenaltyIncrease(), part.exclusivePenaltyAdditive());
+ items.replace(type, newPart);
+ updateGuiForType(type);
+ update();
+ }));
+
+ // exclusive "Increment" item
+ ItemStack exclusiveIncrementItem = new ItemStack(part.exclusivePenaltyIncrease() ? Material.GREEN_TERRACOTTA : Material.RED_TERRACOTTA);
+
+ meta = exclusiveIncrementItem.getItemMeta();
+ meta.setDisplayName(exclusiveIncreasingStr);
+ meta.setLore(List.of(INCREASING_EXPLANATION));
+ meta.setLore(List.of(EXCLUSIVE_EXPLANATION));
+ exclusiveIncrementItem.setItemMeta(meta);
+
+ pane.bindItem(exclusiveIncrement, new GuiItem(exclusiveIncrementItem, (event) -> {
+ event.setCancelled(true);
+
+ WorkPenaltyType.WorkPenaltyPart newPart = new WorkPenaltyType.WorkPenaltyPart(
+ part.penaltyIncrease(), part.penaltyAdditive(),
+ !part.exclusivePenaltyIncrease(), part.exclusivePenaltyAdditive());
+ items.replace(type, newPart);
+ updateGuiForType(type);
+ update();
+ }));
+
+ // exclusive "Additive" item
+ ItemStack exclusiveAdditiveItem = new ItemStack(part.exclusivePenaltyAdditive() ? Material.GREEN_TERRACOTTA : Material.RED_TERRACOTTA);
+
+ meta = exclusiveAdditiveItem.getItemMeta();
+ meta.setDisplayName(exclusiveAdditiveStr);
+ meta.setLore(List.of(ADDING_EXPLANATION));
+ meta.setLore(List.of(EXCLUSIVE_EXPLANATION));
+ exclusiveAdditiveItem.setItemMeta(meta);
+
+ pane.bindItem(exclusiveAdditive, new GuiItem(exclusiveAdditiveItem, (event) -> {
+ event.setCancelled(true);
+
+ WorkPenaltyType.WorkPenaltyPart newPart = new WorkPenaltyType.WorkPenaltyPart(
+ part.penaltyIncrease(), part.penaltyAdditive(),
+ part.exclusivePenaltyIncrease(), !part.exclusivePenaltyAdditive());
+ items.replace(type, newPart);
+ updateGuiForType(type);
+ update();
+ }));
+ }
+
+ @Override
+ public boolean onSave() {
+ return saveWorkPenalty(items);
+ }
+
+ public static boolean saveWorkPenalty(Map partEnum) {
+ ConfigHolder configHolder = ConfigHolder.DEFAULT_CONFIG;
+ FileConfiguration config = configHolder.getConfig();
+
+ partEnum.forEach((key, value) -> {
+ String partPath = key.getPath();
+
+ if (key.getDefaultPenalty().equals(value)) {
+ config.set(partPath, null);
+ return;
+ }
+
+ config.set(partPath + '.' + ConfigOptions.WORK_PENALTY_INCREASE, value.penaltyIncrease());
+ config.set(partPath + '.' + ConfigOptions.WORK_PENALTY_ADDITIVE, value.penaltyAdditive());
+ config.set(partPath + '.' + ConfigOptions.EXCLUSIVE_WORK_PENALTY_INCREASE, value.exclusivePenaltyIncrease());
+ config.set(partPath + '.' + ConfigOptions.EXCLUSIVE_WORK_PENALTY_ADDITIVE, value.exclusivePenaltyAdditive());
+ });
+
+ return configHolder.saveToDisk(true);
+ }
+
+ @Override
+ public boolean hadChange() {
+ for (AnvilUseType type : items.keySet()) {
+ if (!currentType.getPenaltyInfo(type).equals(items.get(type))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/ModrinthUpdateChecker.java b/src/main/java/xyz/alexcrea/cuanvil/update/ModrinthUpdateChecker.java
new file mode 100644
index 0000000..489c636
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/ModrinthUpdateChecker.java
@@ -0,0 +1,214 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2025 Clickism
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package xyz.alexcrea.cuanvil.update;
+
+import com.google.gson.*;
+import org.jetbrains.annotations.Nullable;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Utility class to check for newer versions of a project hosted on Modrinth.
+ */
+public class ModrinthUpdateChecker {
+
+ private static final String API_URL = "https://api.modrinth.com/v2/project/{id}/version";
+
+ private final String projectId;
+ private final String loader;
+ @Nullable
+ private final String minecraftVersion;
+
+ @Nullable
+ private Boolean featured = null;
+
+ @Nullable
+ public Consumer onError = null;
+ @Nullable
+ public Function getRawVersion = ModrinthUpdateChecker::getRawVersion;
+
+ /**
+ * Create a new update checker for the given project.
+ * This will check the latest version for the given loader and any minecraft version.
+ *
+ * @param projectId the project ID
+ * @param loader the loader
+ */
+ public ModrinthUpdateChecker(String projectId, String loader) {
+ this(projectId, loader, null);
+ }
+
+ /**
+ * Create a new update checker for the given project.
+ * This will check the latest version for the given loader and minecraft version.
+ *
+ * @param projectId the project ID
+ * @param loader the loader
+ * @param minecraftVersion the minecraft version, or null for any version
+ */
+ public ModrinthUpdateChecker(String projectId, String loader, @Nullable String minecraftVersion) {
+ this.projectId = projectId;
+ this.loader = loader;
+ this.minecraftVersion = minecraftVersion;
+ }
+
+ /**
+ * Check the latest version of the project for the given loader and minecraft version
+ * and call the consumer with it.
+ *
+ * @param consumer the consumer
+ */
+ public void checkVersion(Consumer consumer) {
+ try {
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(prepareURI())
+ .GET()
+ .build();
+
+ client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
+ .thenAcceptAsync(response -> {
+ if (response.statusCode() != 200) {
+ if(onError != null)
+ onError.accept(new RuntimeException("wrong response status code: " + response.statusCode()));
+ return;
+ }
+ JsonArray versionsArray = JsonParser.parseString(response.body()).getAsJsonArray();
+ String latestVersion = getLatestVersion(versionsArray);
+ if (latestVersion == null) {
+ if(onError != null)
+ onError.accept(new RuntimeException("latest version is null"));
+ return;
+ }
+ consumer.accept(latestVersion);
+ });
+ } catch (Exception e) {
+ if(onError != null) onError.accept(e);
+ }
+ }
+
+ /**
+ * Get the latest compatible version from the versions array.
+ *
+ * @param versions the versions array
+ * @return the latest compatible version
+ */
+ @Nullable
+ protected String getLatestVersion(JsonArray versions) {
+ return versions.asList().stream().findFirst()
+ .map(JsonElement::getAsJsonObject)
+ .map(version -> version.get("version_number").getAsString())
+ .map(getRawVersion != null ? getRawVersion : (v -> v))
+ .orElse(null);
+ }
+
+ /**
+ * Gets the raw version from a version string.
+ * i.E: "fabric-1.2+1.17.1" -> "1.2"
+ *
+ * @param version the version string
+ * @return the raw version string
+ */
+ public static String getRawVersion(String version) {
+ if (version.isEmpty()) return version;
+ version = version.replaceAll("^\\D+", "");
+ String[] split = version.split("\\+");
+ return split[0];
+ }
+
+ /**
+ * Prepare this request uri based on current parameters.
+ * @return the request uri
+ */
+ private URI prepareURI() {
+ var url = new StringBuilder(API_URL.replace("{id}", projectId));
+
+ var parameters = prepareParameters();
+ String[] paramArray = new String[parameters.size()];
+ int i = 0;
+ for (Map.Entry entry : parameters.entrySet()) {
+ paramArray[i++] = entry.getKey() + '=' + entry.getValue();
+ }
+ url.append('?').append(String.join("&", paramArray));
+
+ return URI.create(url.toString());
+ }
+
+ /**
+ * Get the parameters for the version request.
+ *
+ * @return a map of key-value map of the request parameters
+ */
+ private Map prepareParameters(){
+ var parameters = new HashMap();
+
+ parameters.put("loaders", List.of(loader).toString());
+ if(minecraftVersion != null) parameters.put("game_versions", List.of(minecraftVersion).toString());
+ if(featured != null) parameters.put("featured", featured.toString());
+
+ parameters.put("include_changelog", "false");
+ return parameters;
+ }
+
+ /**
+ * Only get featured or non-featured versions.
+ * Null represent no filter.
+ * @param featured should be restricted to featured version ? default null if not called
+ * @return this
+ */
+ public ModrinthUpdateChecker setFeatured(@Nullable Boolean featured) {
+ this.featured = featured;
+ return this;
+ }
+
+ /**
+ * Function called on error calling the api.
+ * @param onError What should happen on error
+ * @return this
+ */
+ public ModrinthUpdateChecker setOnError(@Nullable Consumer onError) {
+ this.onError = onError;
+ return this;
+ }
+
+ /**
+ * Set the function to get raw version from the modrinth version.
+ * If null provided raw version will act as in the identity function.
+ * @param getRawVersion The function transforming modrinth version to raw version
+ * @return this
+ */
+ public ModrinthUpdateChecker setGetRawVersion(@Nullable Function getRawVersion) {
+ this.getRawVersion = getRawVersion;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/PluginSetDefault.java b/src/main/java/xyz/alexcrea/cuanvil/update/PluginSetDefault.java
index 248cc5f..707a218 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/update/PluginSetDefault.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/PluginSetDefault.java
@@ -5,53 +5,90 @@ import io.delilaheve.util.ConfigOptions;
import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
-import xyz.alexcrea.cuanvil.config.WorkPenaltyType;
+import xyz.alexcrea.cuanvil.util.MetricType;
+import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil;
+import xyz.alexcrea.cuanvil.util.config.LoreEditType;
+
+import static io.delilaheve.util.ConfigOptions.*;
+import static xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.*;
public class PluginSetDefault {
- public static void reAddMissingDefault(){
+ public static void reAddMissingDefault() {
FileConfiguration config = ConfigHolder.DEFAULT_CONFIG.getConfig();
int nbSet = 0;
- nbSet+= trySetDefault(config, ConfigOptions.CAP_ANVIL_COST, ConfigOptions.DEFAULT_CAP_ANVIL_COST);
- nbSet+= trySetDefault(config, ConfigOptions.MAX_ANVIL_COST, ConfigOptions.DEFAULT_MAX_ANVIL_COST);
- nbSet+= trySetDefault(config, ConfigOptions.REMOVE_ANVIL_COST_LIMIT, ConfigOptions.DEFAULT_REMOVE_ANVIL_COST_LIMIT);
- nbSet+= trySetDefault(config, ConfigOptions.REPLACE_TOO_EXPENSIVE, ConfigOptions.DEFAULT_REPLACE_TOO_EXPENSIVE);
- nbSet+= trySetDefault(config, ConfigOptions.ITEM_REPAIR_COST, ConfigOptions.DEFAULT_ITEM_REPAIR_COST);
- nbSet+= trySetDefault(config, ConfigOptions.UNIT_REPAIR_COST, ConfigOptions.DEFAULT_UNIT_REPAIR_COST);
- nbSet+= trySetDefault(config, ConfigOptions.ITEM_RENAME_COST, ConfigOptions.DEFAULT_ITEM_RENAME_COST);
- nbSet+= trySetDefault(config, ConfigOptions.SACRIFICE_ILLEGAL_COST, ConfigOptions.DEFAULT_SACRIFICE_ILLEGAL_COST);
- nbSet+= trySetDefault(config, ConfigOptions.ALLOW_COLOR_CODE, ConfigOptions.DEFAULT_ALLOW_COLOR_CODE);
- nbSet+= trySetDefault(config, ConfigOptions.ALLOW_HEXADECIMAL_COLOR, ConfigOptions.DEFAULT_ALLOW_HEXADECIMAL_COLOR);
- nbSet+= trySetDefault(config, ConfigOptions.PERMISSION_NEEDED_FOR_COLOR, ConfigOptions.DEFAULT_PERMISSION_NEEDED_FOR_COLOR);
- nbSet+= trySetDefault(config, ConfigOptions.USE_OF_COLOR_COST, ConfigOptions.DEFAULT_USE_OF_COLOR_COST);
- nbSet+= trySetDefault(config, ConfigOptions.WORK_PENALTY_TYPE, WorkPenaltyType.DEFAULT.configName());
- nbSet+= trySetDefault(config, ConfigOptions.DEFAULT_LIMIT_PATH, ConfigOptions.DEFAULT_ENCHANT_LIMIT);
+ nbSet += trySetDefault(config, METRIC_TYPE, MetricType.AUTO.getValue());
+ nbSet += trySetDefault(config, METRIC_COLLECT_ERROR, true);
- if(nbSet > 0){
+ nbSet += trySetDefault(config, CAP_ANVIL_COST, DEFAULT_CAP_ANVIL_COST);
+ nbSet += trySetDefault(config, MAX_ANVIL_COST, DEFAULT_MAX_ANVIL_COST);
+ nbSet += trySetDefault(config, REMOVE_ANVIL_COST_LIMIT, DEFAULT_REMOVE_ANVIL_COST_LIMIT);
+ nbSet += trySetDefault(config, REPLACE_TOO_EXPENSIVE, DEFAULT_REPLACE_TOO_EXPENSIVE);
+ nbSet += trySetDefault(config, ITEM_REPAIR_COST, DEFAULT_ITEM_REPAIR_COST);
+ nbSet += trySetDefault(config, UNIT_REPAIR_COST, DEFAULT_UNIT_REPAIR_COST);
+ nbSet += trySetDefault(config, ITEM_RENAME_COST, DEFAULT_ITEM_RENAME_COST);
+ nbSet += trySetDefault(config, SACRIFICE_ILLEGAL_COST, DEFAULT_SACRIFICE_ILLEGAL_COST);
+ nbSet += trySetDefault(config, ConfigOptions.ALLOW_COLOR_CODE, ConfigOptions.DEFAULT_ALLOW_COLOR_CODE);
+ nbSet += trySetDefault(config, ALLOW_HEXADECIMAL_COLOR, DEFAULT_ALLOW_HEXADECIMAL_COLOR);
+ nbSet += trySetDefault(config, PERMISSION_NEEDED_FOR_COLOR, DEFAULT_PERMISSION_NEEDED_FOR_COLOR);
+ nbSet += trySetDefault(config, USE_OF_COLOR_COST, DEFAULT_USE_OF_COLOR_COST);
+ nbSet += trySetDefault(config, PER_COLOR_CODE_PERMISSION, DEFAULT_PER_COLOR_CODE_PERMISSION);
+
+ // Lore Edit defaults
+ for (@NotNull LoreEditType value : LoreEditType.values()) {
+ String path = value.getRootPath() + ".";
+
+ nbSet += trySetDefault(config, path + IS_ENABLED, DEFAULT_IS_ENABLED);
+ nbSet += trySetDefault(config, path + FIXED_COST, DEFAULT_FIXED_COST);
+
+ nbSet += trySetDefault(config, path + DO_CONSUME, DEFAULT_DO_CONSUME);
+ if (value.isMultiLine()) {
+ nbSet += trySetDefault(config, path + PER_LINE_COST, DEFAULT_PER_LINE_COST);
+ }
+ if (value.isAppend()) {
+ nbSet += trySetDefault(config, path + LoreEditConfigUtil.ALLOW_COLOR_CODE, LoreEditConfigUtil.DEFAULT_ALLOW_COLOR_CODE);
+ nbSet += trySetDefault(config, path + ALLOW_HEX_COLOR, DEFAULT_ALLOW_HEX_COLOR);
+ nbSet += trySetDefault(config, path + USE_COLOR_COST, DEFAULT_USE_COLOR_COST);
+ } else {
+ nbSet += trySetDefault(config, path + REMOVE_COLOR_COST, DEFAULT_REMOVE_COLOR_COST);
+ }
+ }
+
+ nbSet += trySetDefault(config, BOOK_PERMISSION_NEEDED, DEFAULT_BOOK_PERMISSION_NEEDED);
+ nbSet += trySetDefault(config, PAPER_PERMISSION_NEEDED, DEFAULT_PAPER_PERMISSION_NEEDED);
+
+ nbSet += trySetDefault(config, PAPER_EDIT_ORDER, DEFAULT_PAPER_EDIT_ORDER);
+
+ nbSet += trySetDefault(config, DIALOG_RENAME_ENABLED, DEFAULT_DIALOG_RENAME_ENABLED);
+ nbSet += trySetDefault(config, DIALOG_MAX_SIZE, DEFAULT_DIALOG_MAX_SIZE);
+ nbSet += trySetDefault(config, DIALOG_RENAME_USE_PERMISSION, DEFAULT_DIALOG_RENAME_USE_PERMISSION);
+ nbSet += trySetDefault(config, DIALOG_KEEP_USER_TEXT, DEFAULT_DIALOG_KEEP_USER_TEXT);
+
+ if (nbSet > 0) {
CustomAnvil.instance.getLogger().info("Adding " + nbSet + " absent default config values.");
ConfigHolder.DEFAULT_CONFIG.saveToDisk(true);
}
}
- private static int trySetDefault(@NotNull FileConfiguration config, @NotNull String path, @NotNull String value){
- if(config.isSet(path)) return 0;
+ private static int trySetDefault(@NotNull FileConfiguration config, @NotNull String path, @NotNull String value) {
+ if (config.isSet(path)) return 0;
config.set(path, value);
return 1;
}
- private static int trySetDefault(@NotNull FileConfiguration config, @NotNull String path, int value){
- if(config.isSet(path)) return 0;
+ private static int trySetDefault(@NotNull FileConfiguration config, @NotNull String path, int value) {
+ if (config.isSet(path)) return 0;
config.set(path, value);
return 1;
}
- private static int trySetDefault(@NotNull FileConfiguration config, @NotNull String path, boolean value){
- if(config.isSet(path)) return 0;
+ private static int trySetDefault(@NotNull FileConfiguration config, @NotNull String path, boolean value) {
+ if (config.isSet(path)) return 0;
config.set(path, value);
return 1;
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/UpdateHandler.java b/src/main/java/xyz/alexcrea/cuanvil/update/UpdateHandler.java
new file mode 100644
index 0000000..660accb
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/UpdateHandler.java
@@ -0,0 +1,105 @@
+package xyz.alexcrea.cuanvil.update;
+
+import io.delilaheve.CustomAnvil;
+import xyz.alexcrea.cuanvil.config.ConfigHolder;
+import xyz.alexcrea.cuanvil.update.minecraft.MCUpdate;
+import xyz.alexcrea.cuanvil.update.minecraft.Update_1_21;
+import xyz.alexcrea.cuanvil.update.minecraft.Update_1_21_11;
+import xyz.alexcrea.cuanvil.update.minecraft.Update_1_21_9;
+import xyz.alexcrea.cuanvil.update.plugin.*;
+
+import javax.annotation.Nonnull;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+public class UpdateHandler {
+
+ private static final String CONFIG_VERSION_PATH = "configVersion";
+
+ // Handle mc version update then plugin version update
+ public static void handleUpdates() {
+ handleMCVersionUpdate();
+ handlePluginUpdate();
+ }
+
+ private static final Map>> pUpdateMap = Map.of(
+ new Version(1, 6, 2), PUpdate_1_6_2::handleUpdate,
+ new Version(1, 6, 7), PUpdate_1_6_7::handleUpdate,
+ new Version(1, 8, 0), PUpdate_1_8_0::handleUpdate,
+ new Version(1, 11, 0), PUpdate_1_11_0::handleUpdate,
+ new Version(1, 15, 5), PUpdate_1_15_5::handleUpdate,
+ new Version(1, 15, 6), PUpdate_1_15_6::handleUpdate
+ );
+
+ private static final List mcUpdateMap = List.of(
+ new Update_1_21(),
+ new Update_1_21_9(),
+ new Update_1_21_11()
+ );
+
+ // Handle only plugin update
+ private static void handlePluginUpdate() {
+ String versionString = ConfigHolder.DEFAULT_CONFIG.getConfig().getString(CONFIG_VERSION_PATH);
+ Version current = versionString == null ? new Version(0) : Version.fromString(versionString);
+
+ Set toSave = new HashSet<>();
+
+ AtomicReference latest = new AtomicReference<>(null);
+
+ // Hopefully, should iterate in the "insertion" order
+ pUpdateMap.forEach((ver, consumer) -> {
+ if (ver.greaterThan(current)) {
+ CustomAnvil.log("handling plugin update to " + ver);
+ consumer.accept(toSave);
+
+ latest.set(ver);
+ }
+ });
+
+ if (latest.get() != null) {
+ finishConfiguration(latest.get().toString(), toSave);
+ }
+ }
+
+ // Handle minecraft version update (not plugin version update)
+ public static void handleMCVersionUpdate() {
+ Version current = UpdateUtils.currentMinecraftVersion();
+
+ boolean hadUpdate = false;
+ for (MCUpdate mcUpdate : mcUpdateMap) {
+ hadUpdate |= mcUpdate.handleUpdate(current, hadUpdate);
+ }
+
+ if (hadUpdate) {
+ CustomAnvil.instance.getLogger().info("Updating Done !");
+ }
+
+ if(current.major() == 1 && current.minor() < 21) {
+ var logger = CustomAnvil.instance.getLogger();
+ logger.warning("Your are running an old version of minecraft (lower than 1.21)");
+ logger.warning("Custom Anvil will stop supporting this version on the first of july 2026");
+ }
+ }
+
+ private static void finishConfiguration(@Nonnull String newVersion, @Nonnull Set toSave) {
+ CustomAnvil.instance.getLogger().info("Configuration file updated to " + newVersion);
+ ConfigHolder.DEFAULT_CONFIG.getConfig().set(CONFIG_VERSION_PATH, newVersion);
+
+ toSave.add(ConfigHolder.DEFAULT_CONFIG);
+ // save
+ for (ConfigHolder configHolder : toSave) {
+ configHolder.saveToDisk(true);
+ }
+
+ // then reload
+ for (ConfigHolder configHolder : toSave) {
+ configHolder.reload();
+ }
+
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/UpdateUtils.java b/src/main/java/xyz/alexcrea/cuanvil/update/UpdateUtils.java
index 93d37aa..2907fef 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/update/UpdateUtils.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/UpdateUtils.java
@@ -10,32 +10,27 @@ import java.util.List;
public class UpdateUtils {
public static final String MINECRAFT_VERSION_PATH = "lowMinecraftVersion";
- public static Version currentMinecraftVersion(){
+ public static Version currentMinecraftVersion() {
String versionString = Bukkit.getServer().getBukkitVersion().split("-")[0];
return Version.fromString(versionString);
}
- @Deprecated
- public static int[] currentMinecraftVersionArray(){
- String versionString = Bukkit.getServer().getBukkitVersion().split("-")[0];
- return UpdateUtils.readVersionFromString(versionString);
- }
-
- public static int[] readVersionFromString(String versionString){
- String[] partialVersion = versionString.split("\\.");
- int[] versionParts = new int[]{0, 0, 0};
-
- for (int i = 0; i < Math.min(3, partialVersion.length); i++) {
- versionParts[i] = Integer.parseInt(partialVersion[i]);
- }
- return versionParts;
- }
-
- public static void addToStringList(FileConfiguration config, String path, String... toAdd){
+ public static void addToStringList(FileConfiguration config, String path, String... toAdd) {
List groups = new ArrayList<>(config.getStringList(path));
groups.addAll(Arrays.asList(toAdd));
config.set(path, groups);
}
+ public static void addAbsentToList(FileConfiguration config, String path, String... toAdd) {
+ List groups = new ArrayList<>(config.getStringList(path));
+ for (String val : toAdd) {
+ if (groups.contains(val)) continue;
+
+ groups.add(val);
+ }
+ config.set(path, groups);
+
+ }
+
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/Update_1_21.java b/src/main/java/xyz/alexcrea/cuanvil/update/Update_1_21.java
deleted file mode 100644
index af21989..0000000
--- a/src/main/java/xyz/alexcrea/cuanvil/update/Update_1_21.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package xyz.alexcrea.cuanvil.update;
-
-import io.delilaheve.CustomAnvil;
-import org.bukkit.configuration.file.FileConfiguration;
-import xyz.alexcrea.cuanvil.config.ConfigHolder;
-
-import static xyz.alexcrea.cuanvil.update.UpdateUtils.addToStringList;
-
-// This is a temporary class that aim to handle 1.21 update.
-// It will be replaced by a better system later.
-public class Update_1_21 {
-
- private static final Version V1_21 = new Version(1, 21);
-
- public static void handleUpdate(){
- // Assume if version path is not null then it's 1.21
- String oldVersion = ConfigHolder.DEFAULT_CONFIG.getConfig().getString(UpdateUtils.MINECRAFT_VERSION_PATH);
- if(oldVersion != null){
- Version version = Version.fromString(oldVersion);
-
- // Test 1.21
- if(V1_21.greaterEqual(version)) return;
- }
- Version current = UpdateUtils.currentMinecraftVersion();
-
- // Test 1.21
- if(current.greaterEqual(V1_21)){
- doUpdate();
- }
-
- }
-
- private static void doUpdate() {
- CustomAnvil.instance.getLogger().info("Updating config to support 1.21 ...");
-
- FileConfiguration baseConfig = ConfigHolder.DEFAULT_CONFIG.getConfig();
- FileConfiguration groupConfig = ConfigHolder.ITEM_GROUP_HOLDER.getConfig();
- FileConfiguration conflictConfig = ConfigHolder.CONFLICT_HOLDER.getConfig();
- FileConfiguration unitConfig = ConfigHolder.UNIT_REPAIR_HOLDER.getConfig();
-
- // Add mace to groups
- groupConfig.set("mace.type", "include");
- addToStringList(groupConfig, "mace.items", "mace");
-
- addToStringList(groupConfig, "can_unbreak.groups", "mace");
-
- // Add new enchant conflicts
- addToStringList(conflictConfig, "restriction_density.enchantments", "minecraft:density");
- addToStringList(conflictConfig, "restriction_density.notAffectedGroups", "mace", "enchanted_book");
-
- addToStringList(conflictConfig, "restriction_breach.enchantments", "minecraft:breach");
- addToStringList(conflictConfig, "restriction_breach.notAffectedGroups", "mace", "enchanted_book");
-
- addToStringList(conflictConfig, "restriction_wind_burst.enchantments", "minecraft:wind_burst");
- addToStringList(conflictConfig, "restriction_wind_burst.notAffectedGroups", "mace", "enchanted_book");
-
- // Add mace to conflicts
- addToStringList(conflictConfig, "restriction_fire_aspect.notAffectedGroups", "mace");
- addToStringList(conflictConfig, "restriction_smite.notAffectedGroups", "mace");
- addToStringList(conflictConfig, "restriction_bane_of_arthropods.notAffectedGroups", "mace");
-
- addToStringList(conflictConfig, "mace_enchant_conflict.enchantments",
- "minecraft:density", "minecraft:breach", "minecraft:smite", "minecraft:bane_of_arthropods");
- conflictConfig.set("mace_enchant_conflict.maxEnchantmentBeforeConflict", 1);
-
- // Add level limit
- baseConfig.set("enchant_limits.minecraft:density", 5);
- baseConfig.set("enchant_limits.minecraft:breach", 4);
- baseConfig.set("enchant_limits.minecraft:wind_burst", 3);
-
- // Add enchant values
- baseConfig.set("enchant_values.density.item", 1);
- baseConfig.set("enchant_values.density.book", 1);
-
- baseConfig.set("enchant_values.breach.item", 4);
- baseConfig.set("enchant_values.breach.book", 2);
-
- baseConfig.set("enchant_values.wind_burst.item", 4);
- baseConfig.set("enchant_values.wind_burst.book", 2);
-
- // Add unit repair for mace
- unitConfig.set("breeze_rod.mace", 0.25);
-
- // Set version string as 1.21
- baseConfig.set(UpdateUtils.MINECRAFT_VERSION_PATH, "1.21");
-
- // Save
- ConfigHolder.DEFAULT_CONFIG.saveToDisk(true);
- ConfigHolder.ITEM_GROUP_HOLDER.saveToDisk(true);
- ConfigHolder.CONFLICT_HOLDER.saveToDisk(true);
- ConfigHolder.UNIT_REPAIR_HOLDER.saveToDisk(true);
-
- // imply reload of CONFLICT_HOLDER
- // We also do not need to reload base config as there is no object related to it.
- ConfigHolder.ITEM_GROUP_HOLDER.reload();
-
- CustomAnvil.instance.getLogger().info("Updating Done !");
-
- }
-
-}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/Version.java b/src/main/java/xyz/alexcrea/cuanvil/update/Version.java
index ffa8660..a49fbdd 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/update/Version.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/Version.java
@@ -1,6 +1,9 @@
package xyz.alexcrea.cuanvil.update;
+import org.jetbrains.annotations.NotNull;
+
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
public record Version(int major, int minor, int patch) {
@@ -11,12 +14,18 @@ public record Version(int major, int minor, int patch) {
this(major, 0, 0);
}
- public static Version fromString(@Nonnull String versionString){
+ public static Version fromString(@Nullable String versionString){
+ if(versionString == null) return new Version(0, 0, 0);
+
String[] partialVersion = versionString.split("\\.");
int[] versionParts = new int[]{0, 0, 0};
for (int i = 0; i < Math.min(3, partialVersion.length); i++) {
- versionParts[i] = Integer.parseInt(partialVersion[i]);
+ try {
+ versionParts[i] = Integer.parseInt(partialVersion[i]);
+ } catch (NumberFormatException e) {
+ break;
+ }
}
return new Version(versionParts[0], versionParts[1], versionParts[2]);
}
@@ -45,4 +54,9 @@ public record Version(int major, int minor, int patch) {
this.patch <= other.patch)));
}
+ @NotNull
+ @Override
+ public String toString() {
+ return major + "." + minor + "." + patch;
+ }
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/minecraft/MCUpdate.java b/src/main/java/xyz/alexcrea/cuanvil/update/minecraft/MCUpdate.java
new file mode 100644
index 0000000..40fc587
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/minecraft/MCUpdate.java
@@ -0,0 +1,38 @@
+package xyz.alexcrea.cuanvil.update.minecraft;
+
+import io.delilaheve.CustomAnvil;
+import xyz.alexcrea.cuanvil.config.ConfigHolder;
+import xyz.alexcrea.cuanvil.update.UpdateUtils;
+import xyz.alexcrea.cuanvil.update.Version;
+
+public abstract class MCUpdate {
+
+ public final Version version;
+
+ public MCUpdate(Version version){
+ this.version = version;
+ }
+
+ public boolean handleUpdate(Version current, boolean hadUpdate){
+ // Test if we are running in this update version or better
+ if(version.greaterThan(current))
+ return false;
+
+ // if version path is not null then check if its it's before this update version
+ String oldVersion = ConfigHolder.DEFAULT_CONFIG.getConfig().getString(UpdateUtils.MINECRAFT_VERSION_PATH);
+ if(oldVersion != null){
+ var version = Version.fromString(oldVersion);
+ if(this.version.lesserEqual(version)) return false;
+ }
+
+ if(!hadUpdate){
+ CustomAnvil.instance.getLogger().info("Updating config to support minecraft " + current +" ...");
+ }
+ doUpdate();
+ return true;
+ }
+
+ protected abstract void doUpdate();
+
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/minecraft/Update_1_21.java b/src/main/java/xyz/alexcrea/cuanvil/update/minecraft/Update_1_21.java
new file mode 100644
index 0000000..3aa6073
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/minecraft/Update_1_21.java
@@ -0,0 +1,79 @@
+package xyz.alexcrea.cuanvil.update.minecraft;
+
+import io.delilaheve.CustomAnvil;
+import xyz.alexcrea.cuanvil.config.ConfigHolder;
+import xyz.alexcrea.cuanvil.update.UpdateUtils;
+import xyz.alexcrea.cuanvil.update.Version;
+
+import static xyz.alexcrea.cuanvil.update.UpdateUtils.addAbsentToList;
+
+public class Update_1_21 extends MCUpdate {
+
+ public Update_1_21() {
+ super(new Version(1, 21));
+ }
+
+ @Override
+ protected void doUpdate() {
+ var baseConfig = ConfigHolder.DEFAULT_CONFIG.getConfig();
+ var groupConfig = ConfigHolder.ITEM_GROUP_HOLDER.getConfig();
+ var conflictConfig = ConfigHolder.CONFLICT_HOLDER.getConfig();
+ var unitConfig = ConfigHolder.UNIT_REPAIR_HOLDER.getConfig();
+
+ // Add mace to groups
+ groupConfig.set("mace.type", "include");
+ addAbsentToList(groupConfig, "mace.items", "mace");
+
+ addAbsentToList(groupConfig, "can_unbreak.groups", "mace");
+
+ // Add new enchant conflicts
+ addAbsentToList(conflictConfig, "restriction_density.enchantments", "minecraft:density");
+ addAbsentToList(conflictConfig, "restriction_density.notAffectedGroups", "mace", "enchanted_book");
+
+ addAbsentToList(conflictConfig, "restriction_breach.enchantments", "minecraft:breach");
+ addAbsentToList(conflictConfig, "restriction_breach.notAffectedGroups", "mace", "enchanted_book");
+
+ addAbsentToList(conflictConfig, "restriction_wind_burst.enchantments", "minecraft:wind_burst");
+ addAbsentToList(conflictConfig, "restriction_wind_burst.notAffectedGroups", "mace", "enchanted_book");
+
+ // Add mace to conflicts
+ addAbsentToList(conflictConfig, "restriction_fire_aspect.notAffectedGroups", "mace");
+ addAbsentToList(conflictConfig, "restriction_smite.notAffectedGroups", "mace");
+ addAbsentToList(conflictConfig, "restriction_bane_of_arthropods.notAffectedGroups", "mace");
+
+ addAbsentToList(conflictConfig, "sword_enchant_conflict.enchantments",
+ "minecraft:density", "minecraft:breach");
+
+ // Add level limit
+ baseConfig.set("enchant_limits.minecraft:density", 5);
+ baseConfig.set("enchant_limits.minecraft:breach", 4);
+ baseConfig.set("enchant_limits.minecraft:wind_burst", 3);
+
+ // Add enchant values
+ baseConfig.set("enchant_values.minecraft:density.item", 2);
+ baseConfig.set("enchant_values.minecraft:density.book", 1);
+
+ baseConfig.set("enchant_values.minecraft:breach.item", 4);
+ baseConfig.set("enchant_values.minecraft:breach.book", 2);
+
+ baseConfig.set("enchant_values.minecraft:wind_burst.item", 4);
+ baseConfig.set("enchant_values.minecraft:wind_burst.book", 2);
+
+ // Add unit repair for mace
+ unitConfig.set("breeze_rod.mace", 0.25);
+
+ // Set version string as current
+ baseConfig.set(UpdateUtils.MINECRAFT_VERSION_PATH, version.toString());
+
+ // Save
+ ConfigHolder.DEFAULT_CONFIG.saveToDisk(true);
+ ConfigHolder.ITEM_GROUP_HOLDER.saveToDisk(true);
+ ConfigHolder.CONFLICT_HOLDER.saveToDisk(true);
+ ConfigHolder.UNIT_REPAIR_HOLDER.saveToDisk(true);
+
+ // imply reload of CONFLICT_HOLDER
+ // We also do not need to reload base config as there is no object related to it.
+ ConfigHolder.ITEM_GROUP_HOLDER.reload();
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/minecraft/Update_1_21_11.java b/src/main/java/xyz/alexcrea/cuanvil/update/minecraft/Update_1_21_11.java
new file mode 100644
index 0000000..d639596
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/minecraft/Update_1_21_11.java
@@ -0,0 +1,84 @@
+package xyz.alexcrea.cuanvil.update.minecraft;
+
+import xyz.alexcrea.cuanvil.config.ConfigHolder;
+import xyz.alexcrea.cuanvil.update.UpdateUtils;
+import xyz.alexcrea.cuanvil.update.Version;
+
+import static xyz.alexcrea.cuanvil.update.UpdateUtils.addAbsentToList;
+
+public class Update_1_21_11 extends MCUpdate{
+
+ public Update_1_21_11() {
+ super(new Version(1, 21, 11));
+ }
+
+ @Override
+ protected void doUpdate() {
+ var baseConfig = ConfigHolder.DEFAULT_CONFIG.getConfig();
+ var groupConfig = ConfigHolder.ITEM_GROUP_HOLDER.getConfig();
+ var conflictConfig = ConfigHolder.CONFLICT_HOLDER.getConfig();
+ var unitConfig = ConfigHolder.UNIT_REPAIR_HOLDER.getConfig();
+
+ // Create spear group
+ groupConfig.set("spears.type", "include");
+ addAbsentToList(groupConfig, "spears.items",
+ "wooden_spear",
+ "golden_spear",
+ "stone_spear",
+ "copper_spear",
+ "iron_spear",
+ "diamond_spear",
+ "netherite_spear");
+
+ // Add spear group to super group and enchantments
+ addAbsentToList(groupConfig, "melee_weapons.groups", "spears");
+
+ addAbsentToList(conflictConfig, "restriction_looting.notAffectedGroups", "spears");
+ addAbsentToList(conflictConfig, "restriction_knockback.notAffectedGroups", "spears");
+ addAbsentToList(conflictConfig, "restriction_fire_aspect.notAffectedGroups", "spears");
+
+ // Unit repair for spears
+ unitConfig.set("gold_ingot.golden_spear", 0.25);
+ unitConfig.set("copper_ingot.copper_spear", 0.25);
+ unitConfig.set("iron_ingot.iron_spear", 0.25);
+ unitConfig.set("diamond.diamond_spear", 0.25);
+ unitConfig.set("netherite_ingot.netherite_spear", 0.25);
+
+ unitConfig.set("cobblestone.stone_spear", 0.25);
+ unitConfig.set("cobbled_deepslate.stone_spear", 0.25);
+
+ unitConfig.set("oak_planks.wooden_spear", 0.25);
+ unitConfig.set("spruce_planks.wooden_spear", 0.25);
+ unitConfig.set("birch_planks.wooden_spear", 0.25);
+ unitConfig.set("jungle_planks.wooden_spear", 0.25);
+ unitConfig.set("acacia_planks.wooden_spear", 0.25);
+ unitConfig.set("dark_oak_planks.wooden_spear", 0.25);
+ unitConfig.set("mangrove_planks.wooden_spear", 0.25);
+ unitConfig.set("cherry_planks.wooden_spear", 0.25);
+ unitConfig.set("bamboo_planks.wooden_spear", 0.25);
+ unitConfig.set("crimson_planks.wooden_spear", 0.25);
+ unitConfig.set("warped_planks.wooden_spear", 0.25);
+
+ // Create lunge enchant value and group
+ baseConfig.set("enchant_limits.minecraft:lunge", 3);
+ baseConfig.set("enchant_values.minecraft:lunge.item", 2);
+ baseConfig.set("enchant_values.minecraft:lunge.book", 1);
+
+ addAbsentToList(conflictConfig, "restriction_lunge.enchantments", "minecraft:lunge");
+ addAbsentToList(conflictConfig, "restriction_lunge.notAffectedGroups", "spears", "enchanted_book");
+
+ // Set version string as current
+ baseConfig.set(UpdateUtils.MINECRAFT_VERSION_PATH, version.toString());
+
+ // Save
+ ConfigHolder.DEFAULT_CONFIG.saveToDisk(true);
+ ConfigHolder.ITEM_GROUP_HOLDER.saveToDisk(true);
+ ConfigHolder.CONFLICT_HOLDER.saveToDisk(true);
+ ConfigHolder.UNIT_REPAIR_HOLDER.saveToDisk(true);
+
+ // imply reload of CONFLICT_HOLDER
+ // We also do not need to reload base config as there is no object related to it.
+ ConfigHolder.ITEM_GROUP_HOLDER.reload();
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/minecraft/Update_1_21_9.java b/src/main/java/xyz/alexcrea/cuanvil/update/minecraft/Update_1_21_9.java
new file mode 100644
index 0000000..d289b9b
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/minecraft/Update_1_21_9.java
@@ -0,0 +1,64 @@
+package xyz.alexcrea.cuanvil.update.minecraft;
+
+import org.bukkit.configuration.file.FileConfiguration;
+import xyz.alexcrea.cuanvil.config.ConfigHolder;
+import xyz.alexcrea.cuanvil.update.UpdateUtils;
+import xyz.alexcrea.cuanvil.update.Version;
+
+import static xyz.alexcrea.cuanvil.update.UpdateUtils.addAbsentToList;
+
+public class Update_1_21_9 extends MCUpdate{
+
+ public Update_1_21_9() {
+ super(new Version(1, 21, 9));
+ }
+
+ @Override
+ protected void doUpdate() {
+ var baseConfig = ConfigHolder.DEFAULT_CONFIG.getConfig();
+ var groupConfig = ConfigHolder.ITEM_GROUP_HOLDER.getConfig();
+ var unitConfig = ConfigHolder.UNIT_REPAIR_HOLDER.getConfig();
+
+ // Add cooper items to groups
+ addAbsentToList(groupConfig, "helmets.items", "copper_helmet");
+ addAbsentToList(groupConfig, "chestplate.items", "copper_chestplate");
+ addAbsentToList(groupConfig, "leggings.items", "copper_leggings");
+ addAbsentToList(groupConfig, "boots.items", "copper_boots");
+
+ addAbsentToList(groupConfig, "pickaxes.items", "copper_pickaxe");
+ addAbsentToList(groupConfig, "shovels.items", "copper_shovel");
+ addAbsentToList(groupConfig, "hoes.items", "copper_hoe");
+ addAbsentToList(groupConfig, "axes.items", "copper_axe");
+ addAbsentToList(groupConfig, "swords.items", "copper_sword");
+
+ // Add unit repair
+ addCopperUnitRepair(unitConfig);
+
+ // Set version string as current
+ baseConfig.set(UpdateUtils.MINECRAFT_VERSION_PATH, version.toString());
+
+ // Save
+ ConfigHolder.DEFAULT_CONFIG.saveToDisk(true);
+ ConfigHolder.ITEM_GROUP_HOLDER.saveToDisk(true);
+ ConfigHolder.UNIT_REPAIR_HOLDER.saveToDisk(true);
+
+ // imply reload of CONFLICT_HOLDER
+ // We also do not need to reload base config as there is no object related to it.
+ ConfigHolder.ITEM_GROUP_HOLDER.reload();
+ }
+
+ public static void addCopperUnitRepair(FileConfiguration unitConfig) {
+ // Add unit repair
+ unitConfig.set("copper_ingot.copper_helmet", 0.25);
+ unitConfig.set("copper_ingot.copper_chestplate", 0.25);
+ unitConfig.set("copper_ingot.copper_leggings", 0.25);
+ unitConfig.set("copper_ingot.copper_boots", 0.25);
+
+ unitConfig.set("copper_ingot.copper_pickaxe", 0.25);
+ unitConfig.set("copper_ingot.copper_shovel", 0.25);
+ unitConfig.set("copper_ingot.copper_hoe", 0.25);
+ unitConfig.set("copper_ingot.copper_axe", 0.25);
+ unitConfig.set("copper_ingot.copper_sword", 0.25);
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_11_0.java b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_11_0.java
new file mode 100644
index 0000000..9740971
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_11_0.java
@@ -0,0 +1,137 @@
+package xyz.alexcrea.cuanvil.update.plugin;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import xyz.alexcrea.cuanvil.api.MaterialGroupApi;
+import xyz.alexcrea.cuanvil.config.ConfigHolder;
+import xyz.alexcrea.cuanvil.group.AbstractMaterialGroup;
+import xyz.alexcrea.cuanvil.group.IncludeGroup;
+
+import javax.annotation.Nonnull;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import static xyz.alexcrea.cuanvil.update.UpdateUtils.addAbsentToList;
+
+public class PUpdate_1_11_0 {
+
+ private static final List mace_expected = List.of(
+ "density",
+ "breach",
+ "smite",
+ "bane_of_arthropods"
+ );
+ private static final List sword_expected = List.of(
+ "sharpness",
+ "smite",
+ "bane_of_arthropods"
+ );
+
+ private static final Material[] PICKAXES = new Material[]{
+ Material.WOODEN_PICKAXE, Material.STONE_PICKAXE,
+ Material.IRON_PICKAXE, Material.DIAMOND_PICKAXE,
+ Material.GOLDEN_PICKAXE, Material.NETHERITE_PICKAXE
+ };
+
+ private static final Material[] SHOVELS = new Material[]{
+ Material.WOODEN_SHOVEL, Material.STONE_SHOVEL,
+ Material.IRON_SHOVEL, Material.DIAMOND_SHOVEL,
+ Material.GOLDEN_SHOVEL, Material.NETHERITE_SHOVEL
+ };
+
+ private static final Material[] HOES = new Material[]{
+ Material.WOODEN_HOE, Material.STONE_HOE,
+ Material.IRON_HOE, Material.DIAMOND_HOE,
+ Material.GOLDEN_HOE, Material.NETHERITE_HOE
+ };
+
+ public static void handleUpdate(@Nonnull Set toSave) {
+ handleToolsMigration();
+ handleMaceMigration(toSave);
+ }
+
+ private static void handleToolsMigration() {
+ // We migrate the mace conflict if exist and unmodified
+ AbstractMaterialGroup tools = MaterialGroupApi.getGroup("tools");
+
+ migrateTools(tools, "pickaxes", PICKAXES);
+ migrateTools(tools, "shovels", SHOVELS);
+ migrateTools(tools, "hoes", HOES);
+ }
+
+ private static void migrateTools(
+ @Nullable AbstractMaterialGroup tools,
+ @NotNull String toolset,
+ @NotNull Material[] toolMats) {
+
+ // Create new group
+ IncludeGroup group = new IncludeGroup(toolset);
+ NamespacedKey[] keys = new NamespacedKey[toolMats.length];
+ for (int i = 0; i < toolMats.length; i++) {
+ keys[i] = toolMats[i].getKey();
+ }
+
+ group.addAll(keys);
+
+ MaterialGroupApi.addMaterialGroup(group, true);
+
+ // Try to see if all the materials was in the tools group. and if so, replace it with the new group
+ if (tools == null) return;
+ if (!(tools instanceof IncludeGroup include)) return;
+
+ List mats = List.of(keys);
+ Set matSet = include.getNonGroupInheritedMaterials();
+ if (!matSet.containsAll(mats)) return;
+
+ mats.forEach(matSet::remove);
+ tools.addToPolicy(group);
+ MaterialGroupApi.writeMaterialGroup(tools);
+ }
+
+ private static void handleMaceMigration(@Nonnull Set toSave) {
+ // We migrate the mace conflict if exist and unmodified
+ FileConfiguration config = ConfigHolder.CONFLICT_HOLDER.getConfig();
+
+ if (!config.isConfigurationSection("sword_enchant_conflict")) return;
+ if (!config.isConfigurationSection("mace_enchant_conflict")) return;
+
+ ConfigurationSection mace_conflict = config.getConfigurationSection("mace_enchant_conflict");
+ // Test mace conflict if default
+ if (mace_conflict == null) return;
+ if (mace_conflict.getInt("maxEnchantmentBeforeConflict", 0) != 1) return;
+
+ if (mace_conflict.isList("notAffectedGroups") && !mace_conflict.getList("notAffectedGroups").isEmpty()) return;
+
+ List enchantments = mace_conflict.getStringList("enchantments");
+ if (enchantments.size() != 4) return;
+ for (String ench : mace_expected) {
+ if (!enchantments.contains(ench) && !enchantments.contains("minecraft:" + ench)) return;
+ }
+
+ // Test sword_enchant_conflict is default
+ ConfigurationSection sword_conflict = config.getConfigurationSection("sword_enchant_conflict");
+ if (sword_conflict.getInt("maxEnchantmentBeforeConflict", 0) != 1) return;
+
+ if (sword_conflict.isList("notAffectedGroups") && !sword_conflict.getList("notAffectedGroups").isEmpty())
+ return;
+
+ enchantments = sword_conflict.getStringList("enchantments");
+ if (enchantments.size() != 3) return;
+ for (String ench : sword_expected) {
+ if (!enchantments.contains(ench) && !enchantments.contains("minecraft:" + ench)) return;
+ }
+
+ // Finally we know both conflict are default. so we fix
+ addAbsentToList(config, "sword_enchant_conflict.enchantments",
+ "minecraft:density", "minecraft:breach");
+
+ config.set("mace_enchant_conflict", null);
+ toSave.add(ConfigHolder.CONFLICT_HOLDER);
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_15_5.java b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_15_5.java
new file mode 100644
index 0000000..76f51af
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_15_5.java
@@ -0,0 +1,27 @@
+package xyz.alexcrea.cuanvil.update.plugin;
+
+import org.bukkit.configuration.file.FileConfiguration;
+import xyz.alexcrea.cuanvil.config.ConfigHolder;
+
+import javax.annotation.Nonnull;
+import java.util.Set;
+
+import static xyz.alexcrea.cuanvil.update.UpdateUtils.addAbsentToList;
+
+public class PUpdate_1_15_5 {
+
+ public static void handleUpdate(@Nonnull Set toSave) {
+ FileConfiguration config = ConfigHolder.CONFLICT_HOLDER.getConfig();
+
+ if (config.isConfigurationSection("restriction_luck_of_the_sea")) return;
+
+ // We fix the luck of the see enchantment
+ addAbsentToList(config, "restriction_luck_of_the_sea.enchantments",
+ "minecraft:luck_of_the_sea");
+ addAbsentToList(config, "restriction_luck_of_the_sea.notAffectedGroups",
+ "enchanted_book", "fishing_rod");
+
+ toSave.add(ConfigHolder.CONFLICT_HOLDER);
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_15_6.java b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_15_6.java
new file mode 100644
index 0000000..fde7cfc
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_15_6.java
@@ -0,0 +1,27 @@
+package xyz.alexcrea.cuanvil.update.plugin;
+
+import org.bukkit.configuration.file.FileConfiguration;
+import xyz.alexcrea.cuanvil.config.ConfigHolder;
+import xyz.alexcrea.cuanvil.update.UpdateUtils;
+import xyz.alexcrea.cuanvil.update.Version;
+import xyz.alexcrea.cuanvil.update.minecraft.Update_1_21_9;
+
+import javax.annotation.Nonnull;
+import java.util.Set;
+
+public class PUpdate_1_15_6 {
+
+ public static void handleUpdate(@Nonnull Set toSave) {
+ // fix only needed for 1.21.9 and above
+ Version current = UpdateUtils.currentMinecraftVersion();
+ if (new Version(1, 21, 9).greaterThan(current)) return;
+
+ FileConfiguration unitConfig = ConfigHolder.UNIT_REPAIR_HOLDER.getConfig();
+
+ // Add unit repair
+ Update_1_21_9.addCopperUnitRepair(unitConfig);
+
+ toSave.add(ConfigHolder.UNIT_REPAIR_HOLDER);
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_6_2.java b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_6_2.java
index d004d2d..a4078de 100644
--- a/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_6_2.java
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_6_2.java
@@ -1,13 +1,12 @@
package xyz.alexcrea.cuanvil.update.plugin;
-import io.delilaheve.CustomAnvil;
import org.bukkit.configuration.file.FileConfiguration;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import javax.annotation.Nonnull;
import java.util.Set;
-import static xyz.alexcrea.cuanvil.update.UpdateUtils.addToStringList;
+import static xyz.alexcrea.cuanvil.update.UpdateUtils.addAbsentToList;
public class PUpdate_1_6_2 {
@@ -30,7 +29,7 @@ public class PUpdate_1_6_2 {
}
if(!contained){
- addToStringList(config, path, "enchanted_book");
+ addAbsentToList(config, path, "enchanted_book");
conflictUpdated = true;
}
}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_6_7.java b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_6_7.java
new file mode 100644
index 0000000..ee544dc
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_6_7.java
@@ -0,0 +1,25 @@
+package xyz.alexcrea.cuanvil.update.plugin;
+
+import org.bukkit.configuration.file.FileConfiguration;
+import xyz.alexcrea.cuanvil.config.ConfigHolder;
+
+import javax.annotation.Nonnull;
+import java.util.Set;
+
+public class PUpdate_1_6_7 {
+
+ public static void handleUpdate(@Nonnull Set toSave) {
+ FileConfiguration config = ConfigHolder.DEFAULT_CONFIG.getConfig();
+
+ // We fix the density enchantment
+ String value = config.getString("enchant_values.minecraft:density.item");
+ if(value == null) value = config.getString("enchant_values.density.item");
+
+ if(value == null || "1".equalsIgnoreCase(value)){
+ config.set("enchant_values.minecraft:density.item", 2);
+
+ toSave.add(ConfigHolder.DEFAULT_CONFIG);
+ }
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_8_0.java b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_8_0.java
new file mode 100644
index 0000000..81ce1aa
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_8_0.java
@@ -0,0 +1,66 @@
+package xyz.alexcrea.cuanvil.update.plugin;
+
+import io.delilaheve.util.ConfigOptions;
+import org.bukkit.configuration.file.FileConfiguration;
+import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
+import xyz.alexcrea.cuanvil.config.ConfigHolder;
+import xyz.alexcrea.cuanvil.config.WorkPenaltyType;
+import xyz.alexcrea.cuanvil.gui.config.settings.WorkPenaltyTypeSettingGui;
+
+import javax.annotation.Nonnull;
+import java.util.EnumMap;
+import java.util.Set;
+
+public class PUpdate_1_8_0 {
+
+ private static final String WORK_PENALTY_TYPE = "work_penalty_type";
+
+ public static void handleUpdate(@Nonnull Set toSave) {
+ FileConfiguration config = ConfigHolder.DEFAULT_CONFIG.getConfig();
+
+ // We migrate the work penalty type if it exists
+ String penaltyTypeValue = config.getString(WORK_PENALTY_TYPE);
+ if (penaltyTypeValue == null) return;
+
+ EnumMap partEnum;
+ partEnum = new EnumMap<>(ConfigOptions.INSTANCE.getWorkPenaltyType().getPartMap());
+
+ boolean keepIncrease;
+ boolean keepAdditive;
+
+ switch (penaltyTypeValue.toLowerCase()) {
+ case "add_only":
+ keepIncrease = false;
+ keepAdditive = true;
+ break;
+ case "increase_only":
+ keepIncrease = true;
+ keepAdditive = false;
+ break;
+ case "disabled":
+ keepIncrease = false;
+ keepAdditive = false;
+ break;
+ default:
+ keepIncrease = true;
+ keepAdditive = true;
+ }
+
+ for (AnvilUseType type : partEnum.keySet()) {
+ WorkPenaltyType.WorkPenaltyPart part = partEnum.get(type);
+ part = new WorkPenaltyType.WorkPenaltyPart(
+ keepIncrease & part.penaltyIncrease(),
+ keepAdditive & part.penaltyAdditive(),
+ part.exclusivePenaltyIncrease(),
+ part.exclusivePenaltyAdditive());
+ partEnum.replace(type, part);
+ }
+
+ if(WorkPenaltyTypeSettingGui.saveWorkPenalty(partEnum)){
+ config.set(WORK_PENALTY_TYPE, null);
+ }
+
+ toSave.add(ConfigHolder.DEFAULT_CONFIG);
+ }
+
+}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PluginUpdates.java b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PluginUpdates.java
deleted file mode 100644
index cc0901b..0000000
--- a/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PluginUpdates.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package xyz.alexcrea.cuanvil.update.plugin;
-
-import io.delilaheve.CustomAnvil;
-import xyz.alexcrea.cuanvil.config.ConfigHolder;
-import xyz.alexcrea.cuanvil.update.Version;
-
-import javax.annotation.Nonnull;
-import java.util.HashSet;
-import java.util.Set;
-
-public class PluginUpdates {
-
- private static final String CONFIG_VERSION_PATH = "configVersion";
-
- public static void handlePluginUpdate(){
- String versionString = ConfigHolder.DEFAULT_CONFIG.getConfig().getString(CONFIG_VERSION_PATH);
- Version current = versionString == null ? new Version(0) : Version.fromString(versionString);
-
- Set toSave = new HashSet<>();
-
- if(new Version(1, 6, 2).greaterThan(current)){
- PUpdate_1_6_2.handleUpdate(toSave);
-
- finishConfiguration("1.6.2", toSave);
- }
-
- }
-
- private static void finishConfiguration(@Nonnull String newVersion, @Nonnull Set toSave) {
- CustomAnvil.instance.getLogger().info("Configuration file updated to " + newVersion);
- ConfigHolder.DEFAULT_CONFIG.getConfig().set(CONFIG_VERSION_PATH, newVersion);
-
- toSave.add(ConfigHolder.DEFAULT_CONFIG);
- for (ConfigHolder configHolder : toSave) {
- configHolder.saveToDisk(true);
- }
- }
-
-}
diff --git a/src/main/java/xyz/alexcrea/cuanvil/util/LazyValue.java b/src/main/java/xyz/alexcrea/cuanvil/util/LazyValue.java
new file mode 100644
index 0000000..3ae8fdd
--- /dev/null
+++ b/src/main/java/xyz/alexcrea/cuanvil/util/LazyValue.java
@@ -0,0 +1,36 @@
+package xyz.alexcrea.cuanvil.util;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.function.Supplier;
+
+public class LazyValue {
+
+ private final Supplier valueSupplier;
+ private T storedValue;
+
+ public LazyValue(Supplier valueSupplier) {
+ this.valueSupplier = valueSupplier;
+ this.storedValue = null;
+ }
+
+ @Nullable
+ public T getStored(){
+ return storedValue;
+ }
+
+ @NotNull
+ public T get(){
+ if (storedValue != null) return storedValue;
+
+ synchronized(this) {
+ if(storedValue == null) {
+ storedValue = valueSupplier.get();
+ }
+ }
+
+ return storedValue;
+ }
+
+}
diff --git a/src/main/kotlin/io/delilaheve/CustomAnvil.kt b/src/main/kotlin/io/delilaheve/CustomAnvil.kt
index 285e1c5..4d99faf 100644
--- a/src/main/kotlin/io/delilaheve/CustomAnvil.kt
+++ b/src/main/kotlin/io/delilaheve/CustomAnvil.kt
@@ -6,10 +6,14 @@ import org.bukkit.configuration.file.YamlConfiguration
import org.bukkit.plugin.java.JavaPlugin
import xyz.alexcrea.cuanvil.api.event.CAConfigReadyEvent
import xyz.alexcrea.cuanvil.api.event.CAEnchantRegistryReadyEvent
+import xyz.alexcrea.cuanvil.command.CustomAnvilCommand
import xyz.alexcrea.cuanvil.command.EditConfigExecutor
import xyz.alexcrea.cuanvil.command.ReloadExecutor
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.DependencyManager
+import xyz.alexcrea.cuanvil.dependency.MinecraftVersionUtil
+import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager
+import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant
@@ -17,10 +21,10 @@ import xyz.alexcrea.cuanvil.listener.AnvilCloseListener
import xyz.alexcrea.cuanvil.listener.AnvilResultListener
import xyz.alexcrea.cuanvil.listener.ChatEventListener
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
+import xyz.alexcrea.cuanvil.update.ModrinthUpdateChecker
import xyz.alexcrea.cuanvil.update.PluginSetDefault
-import xyz.alexcrea.cuanvil.update.Update_1_21
-import xyz.alexcrea.cuanvil.update.plugin.PluginUpdates
-import xyz.alexcrea.cuanvil.util.Metrics
+import xyz.alexcrea.cuanvil.update.UpdateHandler
+import xyz.alexcrea.cuanvil.util.MetricsUtil
import java.io.File
import java.io.FileReader
import java.util.logging.Level
@@ -28,11 +32,11 @@ import java.util.logging.Level
/**
* Bukkit/Spigot/Paper plugin to alter anvil feature
*/
-class CustomAnvil : JavaPlugin() {
+open class CustomAnvil : JavaPlugin() {
companion object {
- // bstats plugin id
- private const val bstatsPluginId = 20923
+ // pluginIDS
+ private const val modrinthPluginID = "S75Ueiq9"
// Permission string required to use the plugin's features
const val affectedByPluginPermission = "ca.affected"
@@ -46,14 +50,18 @@ class CustomAnvil : JavaPlugin() {
// Permission string required to reload the config
const val commandReloadPermission = "ca.command.reload"
+ // Permission string required to get diagnostic data
+ const val diagnosticPermission = "ca.command.diagnostic"
+
// Permission string required to edit the plugin's config
const val editConfigPermission = "ca.config.edit"
+
// Command Name to reload the config
const val commandReloadName = "anvilconfigreload"
- // Test command name
- const val commandTestName = "customanvilconfig"
+ // Config command name
+ const val commandConfigName = "customanvilconfig"
// Current plugin instance
lateinit var instance: CustomAnvil
@@ -61,10 +69,12 @@ class CustomAnvil : JavaPlugin() {
// Chat message listener
lateinit var chatListener: ChatEventListener
+ var latestVer: String? = null
+
/**
* Logging handler
*/
- fun log(message: String) {
+ @JvmStatic fun log(message: String) {
if (ConfigOptions.debugLog) {
instance.logger.info(message)
}
@@ -79,7 +89,26 @@ class CustomAnvil : JavaPlugin() {
}
}
+ }
+ // stop plugin if we do not force a dirty start (true by default)
+ // Return true if start was stopped
+ private fun tryDirtyStart(): Boolean {
+ if(!ConfigHolder.DEFAULT_CONFIG.config.getBoolean("dirty_start", false)) {
+ Bukkit.getPluginManager().disablePlugin(this)
+ return true
+ }
+ return false
+ }
+
+ // stop plugin if we force a safe start (false by default)
+ // Return true if start was stopped
+ private fun trySafeStart(): Boolean {
+ if(ConfigHolder.DEFAULT_CONFIG.config.getBoolean("safe_start", false)) {
+ Bukkit.getPluginManager().disablePlugin(this)
+ return true
+ }
+ return false
}
/**
@@ -87,7 +116,74 @@ class CustomAnvil : JavaPlugin() {
*/
override fun onEnable() {
instance = this
+ try {
+ legacyCheck()
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "error trying to check for legacy system", e)
+ MetricsUtil.trackError(e)
+ if(trySafeStart()) return
+ }
+ // Add commands
+ try {
+ prepareCommand()
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "error trying to register commands", e)
+ MetricsUtil.trackError(e)
+ if(trySafeStart()) return
+ }
+
+ // Load default configuration
+ try {
+ if(!ConfigHolder.loadDefaultConfig())
+ throw RuntimeException("Error loading configuration file")
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "error occurred loading default configuration", e)
+ MetricsUtil.trackError(e)
+ if(tryDirtyStart()) return
+ }
+
+ // Load dependency
+ try {
+ DependencyManager.loadDependency()
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "error loading dependency compatibility", e)
+ MetricsUtil.trackError(e)
+ if(tryDirtyStart()) return
+ }
+
+ // Register listeners
+ try {
+ registerListeners()
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "error registering listeners", e)
+ MetricsUtil.trackError(e)
+ if(tryDirtyStart()) return
+ }
+
+ // Load metrics
+ MetricsUtil.loadMetrics(this)
+
+ // Load other thing later.
+ // It is so other dependent plugins can implement there event listener before we fire them.
+ DependencyManager.scheduler.scheduleGlobally(this) { loadEnchantmentSystemDirty() }
+ }
+
+ override fun onDisable() {
+ MetricsUtil.shutdownMetrics()
+ }
+
+ private fun loadEnchantmentSystemDirty() {
+ try {
+ loadEnchantmentSystem()
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "error initializing enchantment system", e)
+ MetricsUtil.trackError(e)
+ tryDirtyStart()
+ }
+ }
+
+ private fun legacyCheck() {
// Disable old plugin name if exist
val potentialPlugin = Bukkit.getPluginManager().getPlugin("UnsafeEnchantsPlus")
if (potentialPlugin != null) {
@@ -96,33 +192,43 @@ class CustomAnvil : JavaPlugin() {
logger.warning("Please note CustomAnvil is a more recent version of UnsafeEnchantsPlus")
}
- // Add commands
- prepareCommand()
-
- // Load chat listener
- chatListener = ChatEventListener()
- server.pluginManager.registerEvents(chatListener, this)
-
- // Load default configuration
- if (!ConfigHolder.loadDefaultConfig()) {
- logger.log(Level.SEVERE,"could not load default config.")
- return
+ val isPaper = PlatformUtil.isPaper
+ if(!isPaper) {
+ logger.warning("It seems you are using spigot")
+ logger.warning("Please take notice that spigot is less supported than paper and derivatives")
+ if(MinecraftVersionUtil.isTooNewForSpigot) {
+ logger.warning("If replace too expensive is not working this is likely because of spigot")
+ logger.warning("As native nms is not supported for spigot starting 26.1")
+ }
}
- // Load dependency
- DependencyManager.loadDependency()
+ val loader = if(isPaper) "paper" else "spigot"
+
+ val version = description.version
+ val featured = if(version.contains("dev")) null else true
+
+ ModrinthUpdateChecker(modrinthPluginID, loader, null)
+ .setFeatured(featured)
+ .setOnError {
+ logger.log(Level.WARNING, "error trying to fetch latest update", it)
+ }
+ .checkVersion { latestVer: String? ->
+ CustomAnvil.latestVer = latestVer
+ if(latestVer == null || version.contains(latestVer)) return@checkVersion
+
+ logger.warning("An update may be available: $latestVer")
+ }
+ }
+
+ private fun registerListeners() {
+ // Register chat listener
+ chatListener = ChatEventListener()
+ server.pluginManager.registerEvents(chatListener, this)
// Register anvil events
server.pluginManager.registerEvents(PrepareAnvilListener(), this)
server.pluginManager.registerEvents(AnvilResultListener(), this)
server.pluginManager.registerEvents(AnvilCloseListener(DependencyManager.packetManager), this)
-
- // Load metrics
- Metrics(this, bstatsPluginId)
-
- // Load other thing later.
- // It is so other dependent plugins can implement there event listener before we fire them.
- DependencyManager.scheduler.scheduleGlobally(this, {loadEnchantmentSystem()})
}
private fun loadEnchantmentSystem(){
@@ -135,15 +241,13 @@ class CustomAnvil : JavaPlugin() {
// Load config
if (!ConfigHolder.loadNonDefaultConfig()) {
- logger.log(Level.SEVERE,"could not load non default config.")
+ logger.log(Level.SEVERE,"Plugin has an issue while trying to load non default config... exiting...")
+ server.pluginManager.disablePlugin(this)
return
}
- // temporary: handle 1.21 update
- Update_1_21.handleUpdate()
-
- // plugin configuration updates
- PluginUpdates.handlePluginUpdate()
+ // Handle minecraft and plugin updates
+ UpdateHandler.handleUpdates()
// Register enchantment of compatible plugin and load configuration change.
DependencyManager.handleCompatibilityConfig()
@@ -156,9 +260,11 @@ class CustomAnvil : JavaPlugin() {
MainConfigGui.getInstance().init(DependencyManager.packetManager)
GuiSharedConstant.loadConstants()
+ // Prepare economy if possible
+ EconomyManager.setupEconomy(this)
+
// Finally, re add default we may be missing
PluginSetDefault.reAddMissingDefault()
-
}
fun reloadResource(
@@ -208,8 +314,10 @@ class CustomAnvil : JavaPlugin() {
var command = getCommand(commandReloadName)
command?.setExecutor(ReloadExecutor())
- command = getCommand(commandTestName)
+ command = getCommand(commandConfigName)
command?.setExecutor(EditConfigExecutor())
+
+ CustomAnvilCommand(this)
}
}
diff --git a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt
index f192ff9..9dc85f9 100644
--- a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt
+++ b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt
@@ -2,9 +2,18 @@ package io.delilaheve.util
import io.delilaheve.CustomAnvil
import io.delilaheve.util.EnchantmentUtil.enchantmentName
+import org.bukkit.NamespacedKey
+import org.bukkit.entity.HumanEntity
+import xyz.alexcrea.cuanvil.anvil.AnvilUseType
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.config.WorkPenaltyType
+import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart
+import xyz.alexcrea.cuanvil.dependency.DependencyManager
+import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
+import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil
+import java.math.BigDecimal
+import java.util.*
/**
* Config option accessors
@@ -15,6 +24,9 @@ object ConfigOptions {
// Path for config values
// ----------------------
+ const val METRIC_TYPE = "metric_type"
+ const val METRIC_COLLECT_ERROR = "metric_collect_errors"
+
const val CAP_ANVIL_COST = "limit_repair_cost"
const val MAX_ANVIL_COST = "limit_repair_value"
const val REMOVE_ANVIL_COST_LIMIT = "remove_repair_limit"
@@ -27,29 +39,56 @@ object ConfigOptions {
const val ITEM_RENAME_COST = "item_rename_cost"
const val SACRIFICE_ILLEGAL_COST = "sacrifice_illegal_enchant_cost"
+ const val ADD_BOOK_ENCHANTMENT_AS_STORED_ENCHANTMENT = "add_book_enchantment_as_stored_enchantment"
// Color related config
const val ALLOW_COLOR_CODE = "allow_color_code"
const val ALLOW_HEXADECIMAL_COLOR = "allow_hexadecimal_color"
+ const val ALLOW_MINIMESSAGE = "allow_minimessage"
const val PERMISSION_NEEDED_FOR_COLOR = "permission_needed_for_color"
const val USE_OF_COLOR_COST = "use_of_color_cost"
- const val WORK_PENALTY_TYPE = "work_penalty_type"
+ const val PER_COLOR_CODE_PERMISSION = "per_color_code_permission"
- const val DEFAULT_LIMIT_PATH = "default_limit"
+ // Work penalty config
+ const val WORK_PENALTY_ROOT = "work_penalty"
+ const val WORK_PENALTY_INCREASE = "shared_increase"
+ const val WORK_PENALTY_ADDITIVE = "shared_additive"
+ const val EXCLUSIVE_WORK_PENALTY_INCREASE = "exclusive_increase"
+ const val EXCLUSIVE_WORK_PENALTY_ADDITIVE = "exclusive_additive"
+
+ // Enchant limit config
+ const val ENCHANT_COUNT_LIMIT_ROOT = "enchantment_count_limit"
+ const val ENCHANT_COUNT_LIMIT_DEFAULT = "$ENCHANT_COUNT_LIMIT_ROOT.default"
+ const val ENCHANT_COUNT_LIMIT_ITEMS = "$ENCHANT_COUNT_LIMIT_ROOT.items"
const val ENCHANT_LIMIT_ROOT = "enchant_limits"
const val ENCHANT_VALUES_ROOT = "enchant_values"
+ // Dialog menu rename
+ const val DIALOG_RENAME_ENABLED = "enable_dialog_rename"
+ const val DIALOG_MAX_SIZE = "dialog_rename_max_size"
+ const val DIALOG_RENAME_USE_PERMISSION = "permission_needed_for_dialog_rename"
+ const val DIALOG_KEEP_USER_TEXT = "dialog_rename_keep_user_text"
+
+ // Others
const val DISABLE_MERGE_OVER_ROOT = "disable-merge-over"
+ const val IMMUTABLE_ENCHANTMENT_LIST = "immutable_enchantments"
+
+ // Monetary configs
+ const val MONETARY_USAGE_ROOT = "monetary_cost"
+ const val SHOULD_USE_MONEY = "$MONETARY_USAGE_ROOT.enabled"
+ const val MONEY_CURRENCY = "$MONETARY_USAGE_ROOT.currency"
+ const val MONETARY_MULTIPLIER_ROOT = "$MONETARY_USAGE_ROOT.multipliers"
+
// Keys for specific enchantment values
private const val KEY_BOOK = "book"
private const val KEY_ITEM = "item"
// Debug flag
- private const val DEBUG_LOGGING = "debug_log"
- private const val VERBOSE_DEBUG_LOGGING = "debug_log_verbose"
+ const val DEBUG_LOGGING = "debug_log"
+ const val VERBOSE_DEBUG_LOGGING = "debug_log_verbose"
// ----------------------
// Default config values
@@ -67,19 +106,34 @@ object ConfigOptions {
const val DEFAULT_ITEM_RENAME_COST = 1
const val DEFAULT_SACRIFICE_ILLEGAL_COST = 1
+ const val DEFAULT_ADD_BOOK_ENCHANTMENT_AS_STORED_ENCHANTMENT = false
+
+ const val DEFAULT_ENCHANT_COUNT_LIMIT = -1
// Color related config
const val DEFAULT_ALLOW_COLOR_CODE = false
const val DEFAULT_ALLOW_HEXADECIMAL_COLOR = false
+ const val DEFAULT_ALLOW_MINIMESSAGE = false
const val DEFAULT_PERMISSION_NEEDED_FOR_COLOR = true
const val DEFAULT_USE_OF_COLOR_COST = 0
- const val DEFAULT_ENCHANT_LIMIT = 5
+ const val DEFAULT_PER_COLOR_CODE_PERMISSION = false
+
+ // Monetary configs
+ const val DEFAULT_SHOULD_USE_MONEY = false
+ const val DEFAULT_MONEY_CURRENCY = "default"
+ const val DEFAULT_MONEY_MULTIPLIER = 1.0
// Debug flag
private const val DEFAULT_DEBUG_LOG = false
private const val DEFAULT_VERBOSE_DEBUG_LOG = false
+ // Dialog menu rename
+ const val DEFAULT_DIALOG_RENAME_ENABLED = false
+ const val DEFAULT_DIALOG_MAX_SIZE = 256
+ const val DEFAULT_DIALOG_RENAME_USE_PERMISSION = false
+ const val DEFAULT_DIALOG_KEEP_USER_TEXT = true
+
// -------------
// Config Ranges
// -------------
@@ -104,16 +158,30 @@ object ConfigOptions {
@JvmField
val USE_OF_COLOR_COST_RANGE = 0..1000
- // Valid range for an enchantment limit
@JvmField
- val ENCHANT_LIMIT_RANGE = 1..255
+ val DIALOG_MAX_SIZE_RANGE = 0..Int.MAX_VALUE
+ // Valid range for an enchantment limit
+ const val ENCHANT_LIMIT = 255
+
+ // Valid range for an enchantment count limit
+ @JvmField
+ val ENCHANT_COUNT_LIMIT_RANGE = -1..255
+
+ // --------------
+ // Other defaults
+ // --------------
// Default value for an enchantment multiplier
private const val DEFAULT_ENCHANT_VALUE = 0
// Default max before merge disabled (negative mean enabled)
- const val DEFAULT_MAX_BEFORE_MERGE_DISABLED = -1;
+ const val DEFAULT_MAX_BEFORE_MERGE_DISABLED = -1
+
+ // -----------
+ // Permissions
+ // -----------
+ private const val RENAME_DIALOG_PERMISSION = "ca.rename.dialog"
// -------------
// Get methods
@@ -209,6 +277,16 @@ object ConfigOptions {
?: DEFAULT_SACRIFICE_ILLEGAL_COST
}
+ /**
+ * Consider book enchantment as book stored enchantment
+ */
+ val addBookEnchantmentAsStoredEnchantment : Boolean
+ get(){
+ return ConfigHolder.DEFAULT_CONFIG
+ .config
+ .getBoolean(ADD_BOOK_ENCHANTMENT_AS_STORED_ENCHANTMENT, DEFAULT_ADD_BOOK_ENCHANTMENT_AS_STORED_ENCHANTMENT)
+ }
+
/**
* Allow usage of color code
*/
@@ -229,10 +307,23 @@ object ConfigOptions {
.getBoolean(ALLOW_HEXADECIMAL_COLOR, DEFAULT_ALLOW_HEXADECIMAL_COLOR)
}
+ /**
+ * Allow usage of minimessage formating
+ */
+ val allowMinimessage: Boolean
+ get() {
+ return ConfigHolder.DEFAULT_CONFIG
+ .config
+ .getBoolean(ALLOW_MINIMESSAGE, DEFAULT_ALLOW_MINIMESSAGE)
+ }
+
/**
* If one of the color component is enabled
*/
- val renameColorPossible: Boolean get() { return allowColorCode || allowHexadecimalColor }
+ val renameColorPossible: Boolean
+ get() {
+ return allowColorCode || allowHexadecimalColor || allowMinimessage
+ }
/**
* If players need a permission to use color
@@ -244,6 +335,16 @@ object ConfigOptions {
.getBoolean(PERMISSION_NEEDED_FOR_COLOR, DEFAULT_PERMISSION_NEEDED_FOR_COLOR)
}
+ /**
+ * Should each color code require a permission
+ */
+ val usePerColorCodePermission: Boolean
+ get() {
+ return ConfigHolder.DEFAULT_CONFIG
+ .config
+ .getBoolean(PER_COLOR_CODE_PERMISSION, DEFAULT_PER_COLOR_CODE_PERMISSION)
+ }
+
/**
* How many xp should use of color should cost
*/
@@ -257,24 +358,77 @@ object ConfigOptions {
}
/**
- * How many xp should use of color should cost
+ * How work penalties should work
*/
val workPenaltyType: WorkPenaltyType
get() {
- return WorkPenaltyType.fromString(
- ConfigHolder.DEFAULT_CONFIG
- .config
- .getString(WORK_PENALTY_TYPE));
+ val penaltyMap = EnumMap(AnvilUseType::class.java)
+
+ for (type in AnvilUseType.entries) {
+ penaltyMap[type] = workPenaltyPart(type)
+ }
+
+ return WorkPenaltyType(penaltyMap)
}
/**
- * Default enchantment limit
+ * How work penalty should work
*/
- private val defaultEnchantLimit: Int
+ fun workPenaltyPart(type: AnvilUseType): WorkPenaltyPart {
+ val config = ConfigHolder.DEFAULT_CONFIG.config
+
+ // Find values
+ val defaultPenalty = type.defaultPenalty
+ val section = config.getConfigurationSection(type.path) ?: return defaultPenalty
+
+ val penaltyIncrease = section.getBoolean(WORK_PENALTY_INCREASE, defaultPenalty.penaltyIncrease)
+ val penaltyAdditive = section.getBoolean(WORK_PENALTY_ADDITIVE, defaultPenalty.penaltyAdditive)
+ val exclusivePenaltyIncrease =
+ section.getBoolean(EXCLUSIVE_WORK_PENALTY_INCREASE, defaultPenalty.exclusivePenaltyIncrease)
+ val exclusivePenaltyAdditive =
+ section.getBoolean(EXCLUSIVE_WORK_PENALTY_ADDITIVE, defaultPenalty.exclusivePenaltyAdditive)
+
+ return WorkPenaltyPart(penaltyIncrease, penaltyAdditive, exclusivePenaltyIncrease, exclusivePenaltyAdditive)
+ }
+
+ /**
+ * Get material enchantment count limit
+ *
+ * @return the current enchantment limit. -1 if none
+ */
+ fun getEnchantCountLimit(type: NamespacedKey): Int? {
+ val limit = materialEnchantCountLimit(type)
+
+ if(limit != null) return limit
+ if(defaultEnchantCountLimit >= 0) return defaultEnchantCountLimit
+
+ return DependencyManager.ecoEnchantCompatibility?.getEcoLevelLimit()
+ }
+
+ /**
+ * Get the material enchantment count limit.
+ *
+ * @return The current enchantment limit. -1 if none
+ */
+ private fun materialEnchantCountLimit(type: NamespacedKey): Int? {
+ val path = "$ENCHANT_COUNT_LIMIT_ITEMS.${type.key.lowercase()}"
+ if(!ConfigHolder.DEFAULT_CONFIG.config.isInt(path))
+ return null
+
+ return ConfigHolder.DEFAULT_CONFIG.config
+ .getInt(path)
+ .takeIf { it in ENCHANT_COUNT_LIMIT_RANGE }
+ }
+ /**
+ * User configured default enchantment count limit
+ */
+ val defaultEnchantCountLimit: Int
get() {
return ConfigHolder.DEFAULT_CONFIG
.config
- .getInt(DEFAULT_LIMIT_PATH, DEFAULT_ENCHANT_LIMIT)
+ .getInt(ENCHANT_COUNT_LIMIT_DEFAULT, DEFAULT_ENCHANT_COUNT_LIMIT)
+ .takeIf { it in ENCHANT_COUNT_LIMIT_RANGE }
+ ?: DEFAULT_ENCHANT_COUNT_LIMIT
}
/**
@@ -297,45 +451,90 @@ object ConfigOptions {
.getBoolean(VERBOSE_DEBUG_LOGGING, DEFAULT_VERBOSE_DEBUG_LOG)
}
+ /**
+ * Is the dialog menu for rename enabled
+ */
+ val doRenameDialog: Boolean
+ get() {
+ return ConfigHolder.DEFAULT_CONFIG
+ .config
+ .getBoolean(DIALOG_RENAME_ENABLED, DEFAULT_DIALOG_RENAME_ENABLED)
+ }
+
+ /**
+ * Do the dialog menu require permission
+ */
+ val doRenameDialogUsePermission: Boolean
+ get() {
+ return ConfigHolder.DEFAULT_CONFIG
+ .config
+ .getBoolean(DIALOG_RENAME_USE_PERMISSION, DEFAULT_DIALOG_RENAME_USE_PERMISSION)
+ }
+
+ fun canUseDialogRename(player: HumanEntity): Boolean {
+ if(!doRenameDialog || !AnvilRenameDialogUtil.anvilRenameDialog.canSendDialog()) return false
+ if(doRenameDialogUsePermission && !player.hasPermission(RENAME_DIALOG_PERMISSION)) return false
+
+ return true
+ }
+
+ /**
+ * Do the dialog menu require permission
+ */
+ val renameDialogMaxSize: Int
+ get() {
+ return ConfigHolder.DEFAULT_CONFIG
+ .config
+ .getInt(DIALOG_MAX_SIZE, DEFAULT_DIALOG_MAX_SIZE)
+ .takeIf { it in DIALOG_MAX_SIZE_RANGE }
+ ?: Int.MAX_VALUE
+ }
+
+ /**
+ * Should the text used for rename should be kept in the item's pdc
+ */
+ val shouldKeepRenameText: Boolean
+ get() {
+ return ConfigHolder.DEFAULT_CONFIG
+ .config
+ .getBoolean(DIALOG_KEEP_USER_TEXT, DEFAULT_DIALOG_KEEP_USER_TEXT)
+ }
+
/**
* Get the given [enchantment]'s limit
*/
fun enchantLimit(enchantment: CAEnchantment): Int {
+ val limit = rawEnchantLimit(enchantment)
+ if(limit >= 0) return limit.coerceAtMost(ENCHANT_LIMIT)
+
+ // get default
+ return enchantment.defaultMaxLevel()
+ }
+
+ /**
+ * Get the given [enchantment]'s limit
+ */
+ fun rawEnchantLimit(enchantment: CAEnchantment): Int {
// Test namespace
var limit = enchantLimit(enchantment.key.toString())
- if(limit != null) return limit;
+ if (limit >= 0) return limit
// Test legacy (name only)
limit = enchantLimit(enchantment.enchantmentName)
- if(limit != null) return limit;
+ if (limit >= 0) return limit
- // get default (and test old legacy if present)
- return getDefaultLevel(enchantment.enchantmentName)
+ // Default to negative
+ return -1
}
/**
* Get the given [enchantmentName]'s limit
*/
- private fun enchantLimit(enchantmentName: String): Int? {
+ private fun enchantLimit(enchantmentName: String): Int {
val path = "${ENCHANT_LIMIT_ROOT}.$enchantmentName"
- return CustomAnvil.instance
- .config
- .getInt(path, ENCHANT_LIMIT_RANGE.first-1)
- .takeIf { it in ENCHANT_LIMIT_RANGE }
- }
-
- /**
- * Get default value if enchantment do not exist on config
- */
- private fun getDefaultLevel(enchantmentName: String, // compatibility with 1.20.5. TODO better update system
- ) : Int {
- if(enchantmentName == "sweeping_edge"){
- val limit = enchantLimit("sweeping")
- if(limit != null) return limit
-
- }
- return defaultEnchantLimit
+ return CustomAnvil.instance.config
+ .getInt(path, -1)
}
/**
@@ -348,11 +547,11 @@ object ConfigOptions {
): Int {
// Test namespace
var limit = enchantmentValue(enchantment.key.toString(), isFromBook)
- if(limit != null) return limit;
+ if (limit != null) return limit
// Test legacy (name only)
limit = enchantmentValue(enchantment.enchantmentName, isFromBook)
- if(limit != null) return limit;
+ if (limit != null) return limit
// get default (and test old legacy if present)
return getDefaultValue(enchantment, isFromBook)
@@ -378,21 +577,23 @@ object ConfigOptions {
/**
* Get default value if enchantment do not exist on config
*/
- private fun getDefaultValue(enchantment: CAEnchantment, // compatibility with 1.20.5. TODO better update system
- isFromBook: Boolean) : Int {
+ private fun getDefaultValue(
+ enchantment: CAEnchantment, // compatibility with 1.20.5. TODO better update system
+ isFromBook: Boolean
+ ): Int {
val enchantmentName = enchantment.key.toString()
- if(enchantmentName == "minecraft:sweeping_edge"){
+ if (enchantmentName == "minecraft:sweeping_edge") {
var limit = enchantmentValue("minecraft:sweeping", isFromBook)
- if(limit != null) return limit
-
+ if (limit != null) return limit
+
// legacy name
limit = enchantmentValue("sweeping", isFromBook)
- if(limit != null) return limit
+ if (limit != null) return limit
}
val rarity = enchantment.defaultRarity()
- return if(isFromBook)
+ return if (isFromBook)
rarity.bookValue
else
rarity.itemValue
@@ -405,20 +606,20 @@ object ConfigOptions {
fun maxBeforeMergeDisabled(enchantment: CAEnchantment): Int {
val key = enchantment.key.toString()
var value = maxBeforeMergeDisabled(key)
- if(value != null) return value
+ if (value >= 0) return value
// Legacy name
val legacy = enchantment.enchantmentName
value = maxBeforeMergeDisabled(legacy)
- if(value != null) return value
+ if (value >= 0) return value
- if(key == "minecraft:sweeping_edge"){
+ if (key == "minecraft:sweeping_edge") {
value = maxBeforeMergeDisabled("minecraft:sweeping")
- if(value != null) return value
+ if (value >= 0) return value
// legacy name of legacy enchantment name
value = maxBeforeMergeDisabled("sweeping")
- if(value != null) return value
+ if (value >= 0) return value
}
return DEFAULT_MAX_BEFORE_MERGE_DISABLED
@@ -428,14 +629,51 @@ object ConfigOptions {
* Get the given [enchantmentName]'s level before merge is disabled
* a negative value would mean never disabled
*/
- private fun maxBeforeMergeDisabled(enchantmentName: String) : Int? {
+ private fun maxBeforeMergeDisabled(enchantmentName: String): Int {
// find if set
val path = "${DISABLE_MERGE_OVER_ROOT}.$enchantmentName"
return CustomAnvil.instance
.config
- .getInt(path, ENCHANT_LIMIT_RANGE.min() - 1)
- .takeIf { it in ENCHANT_LIMIT_RANGE }
+ .getInt(path, -1)
+ }
+
+ fun isImmutable(key: NamespacedKey): Boolean {
+ val immutables = ConfigHolder.DEFAULT_CONFIG.config.getStringList(IMMUTABLE_ENCHANTMENT_LIST)
+
+ // We need to ignore case so can't just check "contain"
+ for (ench in immutables) {
+ if (ench.equals(key.toString(), ignoreCase = true) ||
+ ench.equals(key.key, ignoreCase = true)
+ )
+ return true
+ }
+ return false
+ }
+
+ /*
+ * Monetary configs (only for 1.21.6+)
+ * Also require dialog rename
+ */
+ fun shouldUseMoney(player: HumanEntity): Boolean {
+ return EconomyManager.economy?.initialized() == true &&
+ canUseDialogRename(player) &&
+ ConfigHolder.DEFAULT_CONFIG
+ .config
+ .getBoolean(SHOULD_USE_MONEY, DEFAULT_SHOULD_USE_MONEY)
+ }
+
+ val usedCurrency: String
+ get() {
+ return ConfigHolder.DEFAULT_CONFIG
+ .config
+ .getString(MONEY_CURRENCY, DEFAULT_MONEY_CURRENCY)!!
+ }
+
+ fun getMonetaryMultiplier(type: String): BigDecimal {
+ return BigDecimal(ConfigHolder.DEFAULT_CONFIG
+ .config
+ .getDouble("$MONETARY_MULTIPLIER_ROOT.$type", DEFAULT_MONEY_MULTIPLIER))
}
}
diff --git a/src/main/kotlin/io/delilaheve/util/EnchantmentUtil.kt b/src/main/kotlin/io/delilaheve/util/EnchantmentUtil.kt
index 2cde74c..af959f2 100644
--- a/src/main/kotlin/io/delilaheve/util/EnchantmentUtil.kt
+++ b/src/main/kotlin/io/delilaheve/util/EnchantmentUtil.kt
@@ -6,6 +6,7 @@ import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.group.ConflictType
+import xyz.alexcrea.cuanvil.util.MaterialUtil.customType
import kotlin.math.max
import kotlin.math.min
@@ -30,64 +31,97 @@ object EnchantmentUtil {
) = mutableMapOf().apply {
putAll(this@combineWith)
- other.forEach { (enchantment, level) ->
- if(!enchantment.isAllowed(player)) return@forEach
+ CustomAnvil.verboseLog("Testing merge")
+ val bypassFuse = player.hasPermission(CustomAnvil.bypassFusePermission)
+ val bypassLevel = player.hasPermission(CustomAnvil.bypassLevelPermission)
+ var maxEnchantCount = ConfigOptions.getEnchantCountLimit(item.customType)
+ if(maxEnchantCount == null || maxEnchantCount < 0) maxEnchantCount = Int.MAX_VALUE
+
+ val allowed = other.filter { (enchantment, _) -> enchantment.isAllowed(player) }
+ val new = allowed.filter{ (enchantment, _) -> !containsKey(enchantment)}
+ val old = allowed.filter{ (enchantment, _) -> containsKey(enchantment)}
+
+ fun maxLevel(enchantment: CAEnchantment): Int {
+ val max = if (bypassLevel) { 255 }
+ else { ConfigOptions.enchantLimit(enchantment) }
+
+ CustomAnvil.verboseLog("Max level of ${enchantment.key} is $max (bypassLevel is $bypassLevel)")
+ return max
+ }
+
+ old.forEach { (enchantment, level) ->
// Get max level or 255 if player can bypass
- val maxLevel = if (player.hasPermission(CustomAnvil.bypassLevelPermission))
- { 255 } else
- { ConfigOptions.enchantLimit(enchantment) }
-
+ val maxLevel = maxLevel(enchantment)
val cappedLevel = min(level, maxLevel)
- // Enchantment not yet in result list
- if (!containsKey(enchantment)) {
- // Add the enchantment if it doesn't have conflicts, or if player is allowed to bypass enchantment restrictions
- this[enchantment] = cappedLevel
- val conflictType =
- ConfigHolder.CONFLICT_HOLDER.conflictManager.isConflicting(this, item, enchantment)
- if (!player.hasPermission(CustomAnvil.bypassFusePermission) &&
- (conflictType != ConflictType.NO_CONFLICT)
- ) {
- CustomAnvil.verboseLog("Enchantment not yet in result list, but there is conflict (${enchantment.key}, conflict: $conflictType)")
- this.remove(enchantment)
+ val oldLevel = this[enchantment]!! // <- should not be null. (enchantment already in result list)
+
+ // ... and they're not the same level
+ if (oldLevel != cappedLevel) {
+ // apply the greater of the two or left one if right is above max
+ this[enchantment] = max(oldLevel, cappedLevel)
+ }
+ // ... and they're the same level
+ else {
+ // We test if it is allowed to merge at this level
+ if(!bypassLevel){
+ val maxBeforeDisabled = ConfigOptions.maxBeforeMergeDisabled(enchantment)
+ if((maxBeforeDisabled > 0) && (oldLevel >= maxBeforeDisabled)) {
+ CustomAnvil.verboseLog(
+ "Reached max merge before disable for ${enchantment.key}: $oldLevel/$maxBeforeDisabled)")
+ return@forEach
+ }
}
+ // Now we increase the enchantment level by 1
+ var newLevel = oldLevel + 1
+ newLevel = max(min(newLevel, maxLevel), oldLevel)
+ this[enchantment] = newLevel
}
- // Enchantment already in result list
- else {
- val oldLevel = this[enchantment]!! // <- should not be null. (enchantment already in result list)
+
+ if(bypassFuse){
+ CustomAnvil.verboseLog("Bypassed conflict check for ${enchantment.key}")
+ } else {
+ val conflictType = ConfigHolder.CONFLICT_HOLDER.conflictManager
+ .isConflicting(this, item, enchantment)
// ... and they are conflicting
- val conflictType =
- ConfigHolder.CONFLICT_HOLDER.conflictManager.isConflicting(this, item, enchantment)
- if ((conflictType != ConflictType.NO_CONFLICT)
- && !player.hasPermission(CustomAnvil.bypassFusePermission)
- ) {
- CustomAnvil.verboseLog("Enchantment already in result list, and they are conflicting (${enchantment.key}, conflict: $conflictType)")
+ if(conflictType != ConflictType.NO_CONFLICT){
+ CustomAnvil.verboseLog(
+ "Enchantment already in result list, and they are conflicting (${enchantment.key}, conflict: $conflictType)")
+ this[enchantment] = oldLevel
return@forEach
}
-
- // ... and they're not the same level
- if (oldLevel != cappedLevel) {
- // apply the greater of the two or left one if right is above max
- this[enchantment] = max(oldLevel, cappedLevel)
-
- }
- // ... and they're the same level
- else {
- // We test if it is allowed to merge at this level
- val maxBeforeDisabled = ConfigOptions.maxBeforeMergeDisabled(enchantment)
- if((maxBeforeDisabled > 0) && (oldLevel >= maxBeforeDisabled)) return@forEach
-
- // Now we increase the enchantment level by 1
- var newLevel = oldLevel + 1
- newLevel = max(min(newLevel, maxLevel), oldLevel)
- this[enchantment] = newLevel
-
- }
}
}
+
+ // Try to add new now
+ new.forEach { (enchantment, level) ->
+ // Get max level or 255 if player can bypass
+ val maxLevel = maxLevel(enchantment)
+ val cappedLevel = min(level, maxLevel)
+
+ // Do not allow new enchantment if above maximum
+ if(this.size >= maxEnchantCount) return@forEach
+
+ // Add the enchantment if it doesn't have conflicts, or if player is allowed to bypass enchantment restrictions
+ this[enchantment] = cappedLevel
+ if(bypassFuse){
+ CustomAnvil.verboseLog("Bypassed conflict check for ${enchantment.key}")
+ return@forEach
+ }
+
+ val conflictType = ConfigHolder.CONFLICT_HOLDER.conflictManager
+ .isConflicting(this, item, enchantment)
+
+ if (conflictType != ConflictType.NO_CONFLICT) {
+ CustomAnvil.verboseLog("Enchantment not yet in result list, but there is conflict (${enchantment.key}, conflict: $conflictType)")
+ this.remove(enchantment)
+ }
+
+ }
+
}
}
diff --git a/src/main/kotlin/io/delilaheve/util/ItemUtil.kt b/src/main/kotlin/io/delilaheve/util/ItemUtil.kt
index a85af39..25698ad 100644
--- a/src/main/kotlin/io/delilaheve/util/ItemUtil.kt
+++ b/src/main/kotlin/io/delilaheve/util/ItemUtil.kt
@@ -4,6 +4,9 @@ import org.bukkit.Material.ENCHANTED_BOOK
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
+import xyz.alexcrea.cuanvil.update.UpdateUtils
+import xyz.alexcrea.cuanvil.util.MaterialUtil.customType
+import xyz.alexcrea.cuanvil.util.MaxDamageCheckerUtil
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
@@ -35,6 +38,13 @@ object ItemUtil {
}
+ private fun maxDamage(damageable: Damageable): Int {
+ val ver = UpdateUtils.currentMinecraftVersion()
+ if(ver.major <= 1 && ver.minor <= 20 && ver.patch < 5) return Integer.MAX_VALUE
+
+ return MaxDamageCheckerUtil.getMaxDamage(damageable)
+ }
+
/**
* Set this [ItemStack]s durability from a combination of the
* [first] and [second] item's durability values
@@ -54,7 +64,9 @@ object ItemUtil {
val secondDurability = durability - secondDamage
val combinedDurability = firstDurability + secondDurability
val newDurability = min(combinedDurability, durability)
- it.damage = durability - newDurability
+
+ val maxDamage = maxDamage(it)
+ it.damage = min(durability - newDurability, maxDamage)
itemMeta = it
return true
}
@@ -90,5 +102,5 @@ object ItemUtil {
*/
fun ItemStack.canMergeWith(
other: ItemStack?
- ) = (other != null) && (type == other.type || (other.isEnchantedBook()))
+ ) = (other != null) && (customType == other.customType || (other.isEnchantedBook()))
}
diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilCost.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilCost.kt
new file mode 100644
index 0000000..710687c
--- /dev/null
+++ b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilCost.kt
@@ -0,0 +1,72 @@
+package xyz.alexcrea.cuanvil.anvil
+
+import io.delilaheve.util.ConfigOptions
+import java.math.BigDecimal
+import kotlin.math.min
+import io.delilaheve.util.ConfigOptions.getMonetaryMultiplier as moneyMultiplier
+
+open class AnvilCost {
+ private val isAlone: Boolean
+ var valid = true // Get set as invalid if cost can be satisfied
+ var isMonetary = false
+
+ var generic = 0
+ var enchantment = 0
+ var repair = 0
+ var rename = 0
+ var lore = 0
+ var illegalPenalty = 0
+ var workPenalty = 0
+ var recipe = 0
+
+ constructor(generic: Int) {
+ this.generic = generic
+ isAlone = true
+ }
+
+ constructor() {
+ isAlone = false
+ }
+
+ fun asXpCost(): Int {
+ return generic + enchantment + repair + rename + lore + illegalPenalty + workPenalty + recipe
+ }
+
+ fun filteredXpCost(ignoreRules: Boolean = false): Int {
+ val original = asXpCost()
+
+ // Test repair cost limit
+ return if (
+ !ignoreRules &&
+ !ConfigOptions.doRemoveCostLimit &&
+ ConfigOptions.doCapCost
+ ) {
+ min(original, ConfigOptions.maxAnvilCost)
+ } else {
+ original
+ }
+ }
+
+ open fun asMonetaryCost(): BigDecimal {
+ // multiply by per use type multipliers
+ return BigDecimal(generic)
+ .add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment")))
+ .add(BigDecimal(repair).multiply(moneyMultiplier("repair")))
+ .add(BigDecimal(rename).multiply(moneyMultiplier("rename")))
+ .add(BigDecimal(lore).multiply(moneyMultiplier("lore_edit")))
+ .add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment")))
+ .add(BigDecimal(illegalPenalty).multiply(moneyMultiplier("work_penalty")))
+ .add(BigDecimal(workPenalty).multiply(moneyMultiplier("work_penalty")))
+ .add(BigDecimal(recipe).multiply(moneyMultiplier("recipe")))
+ .multiply(moneyMultiplier("global"))
+ }
+}
+
+class CustomCraftCost(val rawCost: Int): AnvilCost() {
+
+ override fun asMonetaryCost(): BigDecimal {
+ return BigDecimal(rawCost)
+ .multiply(moneyMultiplier("global"))
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt
new file mode 100644
index 0000000..6b106fc
--- /dev/null
+++ b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt
@@ -0,0 +1,331 @@
+package xyz.alexcrea.cuanvil.anvil
+
+import io.delilaheve.CustomAnvil
+import io.delilaheve.util.ConfigOptions
+import io.delilaheve.util.EnchantmentUtil.combineWith
+import io.delilaheve.util.ItemUtil.findEnchantments
+import io.delilaheve.util.ItemUtil.isEnchantedBook
+import io.delilaheve.util.ItemUtil.repairFrom
+import io.delilaheve.util.ItemUtil.setEnchantmentsUnsafe
+import io.delilaheve.util.ItemUtil.unitRepair
+import org.bukkit.ChatColor
+import org.bukkit.Material
+import org.bukkit.entity.HumanEntity
+import org.bukkit.entity.Player
+import org.bukkit.inventory.AnvilInventory
+import org.bukkit.inventory.InventoryView
+import org.bukkit.inventory.ItemStack
+import org.bukkit.inventory.meta.ItemMeta
+import org.bukkit.persistence.PersistentDataType
+import xyz.alexcrea.cuanvil.dependency.DependencyManager
+import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog
+import xyz.alexcrea.cuanvil.enchant.CAEnchantment
+import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe
+import xyz.alexcrea.cuanvil.util.CasedStringUtil
+import xyz.alexcrea.cuanvil.util.CustomRecipeUtil
+import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir
+import xyz.alexcrea.cuanvil.util.MiniMessageUtil
+import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
+import xyz.alexcrea.cuanvil.util.anvil.AnvilColorUtil
+import xyz.alexcrea.cuanvil.util.anvil.AnvilLoreEditUtil
+import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil
+import xyz.alexcrea.cuanvil.util.config.LoreEditType
+import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil
+
+object AnvilMergeLogic {
+
+ open class AnvilResult {
+ companion object {
+ val EMPTY = AnvilResult(null, AnvilCost())
+ }
+
+ val item: ItemStack?
+ val cost: AnvilCost
+ val ignoreXpRules: Boolean
+
+ constructor(item: ItemStack?, cost: AnvilCost, ignoreXpRules: Boolean = false) {
+ this.item = item
+ this.cost = cost
+ this.ignoreXpRules = ignoreXpRules
+ }
+
+ fun isEmpty(): Boolean {
+ return item == null
+ }
+ }
+
+ class UnitRepairResult : AnvilResult {
+ companion object {
+ val EMPTY = UnitRepairResult(null, AnvilCost(), 0)
+ }
+
+ val repairAmount: Int
+
+ constructor(item: ItemStack?, cost: AnvilCost, repairAmount: Int) : super(item, cost) {
+ this.repairAmount = repairAmount
+ }
+ }
+
+ class CustomCraftResult : AnvilResult {
+ companion object {
+ val EMPTY = CustomCraftResult(null, CustomCraftCost(0), 0, null)
+ }
+
+ val customCraftCost: CustomCraftCost
+ val amount: Int
+ val recipe: AnvilCustomRecipe?
+
+ constructor(
+ item: ItemStack?, cost: CustomCraftCost,
+ amount: Int, recipe: AnvilCustomRecipe?
+ ) : super(item, cost, true) {
+ this.customCraftCost = cost
+ this.amount = amount
+ this.recipe = recipe
+ }
+ }
+
+ class LoreEditResult : AnvilResult {
+ companion object {
+ val EMPTY = LoreEditResult(null, AnvilCost(), LoreEditType.APPEND_PAPER)
+ }
+
+ val type: LoreEditType
+
+ constructor(item: ItemStack?, cost: AnvilCost, type: LoreEditType) : super(item, cost) {
+ this.type = type
+ }
+ }
+
+ fun doRenaming(
+ view: InventoryView, //TODO use anvil view
+ inventory: AnvilInventory,
+ player: Player, first: ItemStack
+ ): AnvilResult {
+ val resultItem = DependencyManager.cloneItem(player, first)
+ val cost = AnvilCost()
+ cost.rename = handleRename(resultItem, inventory, player)
+
+ // Test/stop if nothing changed.
+ if (first == resultItem) {
+ CustomAnvil.log("no right item, But input is same as output")
+ return AnvilResult.EMPTY
+ }
+
+ cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY)
+ val result =
+ DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.RENAME_ONLY, cost)
+
+ return AnvilResult(result, cost)
+ }
+
+ private fun processDialogPCD(meta: ItemMeta, player: HumanEntity) {
+ val text = AnvilRenameDialogUtil.anvilRenameDialog.currentText(player)
+ return processPCD(meta, player, text)
+ }
+
+ fun processPCD(meta: ItemMeta, player: HumanEntity, text: String?) {
+ val keepDialog = ConfigOptions.canUseDialogRename(player) && ConfigOptions.shouldKeepRenameText
+
+ val pdc = meta.persistentDataContainer
+ if (!keepDialog)
+ pdc.remove(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY)
+ else {
+ if (text == null || text.isBlank())
+ pdc.remove(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY)
+ else pdc.set(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY, PersistentDataType.STRING, text)
+ }
+ }
+
+ private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory, player: HumanEntity): Int {
+ // Can be null
+ var renameText = ChatColor.stripColor(inventory.renameText)
+
+ var sumCost = 0
+ var useColor = false
+ if (ConfigOptions.renameColorPossible && renameText != null) {
+ val component = AnvilColorUtil.handleColor(
+ renameText,
+ AnvilColorUtil.renamePermission(player)
+ )
+
+ if (component != null) {
+ renameText = MiniMessageUtil.legacy_mm.serialize(component)
+
+ sumCost += ConfigOptions.useOfColorCost
+ useColor = true
+ }
+ }
+
+ // Rename item and add renaming cost
+ resultItem.itemMeta?.let {
+ val hasDisplayName = it.hasDisplayName()
+ val displayName = if (!hasDisplayName) null
+ else if (useColor) it.displayName
+ else ChatColor.stripColor(it.displayName)
+
+
+ if (!displayName.contentEquals(renameText) && !(displayName == null &&
+ renameText == "" ||
+ //TODO on recent paper check effective name instead
+ renameText == CasedStringUtil.snakeToUpperSpacedCase(resultItem.type.name.lowercase())
+ )
+ ) {
+ it.setDisplayName(renameText)
+ processDialogPCD(it, player)
+ resultItem.itemMeta = it
+
+ sumCost += ConfigOptions.itemRenameCost
+ }
+
+ return sumCost
+ }
+ return 0
+ }
+
+ fun doMerge(
+ view: InventoryView, //TODO use anvil view instead
+ inventory: AnvilInventory,
+ player: Player,
+ first: ItemStack, second: ItemStack
+ ): AnvilResult {
+ val newEnchants = first.findEnchantments()
+ .combineWith(second.findEnchantments(), first, player)
+ var hasChanged = !isIdentical(first.findEnchantments(), newEnchants)
+
+ val resultItem = DependencyManager.cloneItem(player, first)
+ val cost = AnvilCost()
+ if (hasChanged) {
+ resultItem.setEnchantmentsUnsafe(newEnchants)
+ // Calculate enchantment cost
+ AnvilXpUtil.getRightValues(second, resultItem, cost)
+ }
+
+ // Calculate repair cost
+ if (!first.isEnchantedBook() && !second.isEnchantedBook()) {
+ // we only need to be concerned with repair when neither item is a book
+ val repaired = resultItem.repairFrom(first, second)
+ cost.repair = if (repaired) ConfigOptions.itemRepairCost else 0
+ hasChanged = hasChanged || repaired
+ }
+
+ // Test/stop if nothing changed.
+ if (!hasChanged) {
+ CustomAnvil.log("Mergeable with second, But input is same as output")
+ return AnvilResult.EMPTY
+ }
+ // As calculatePenalty edit result, we need to calculate penalty after checking equality
+ cost.workPenalty = AnvilXpUtil.calculatePenalty(first, second, resultItem, AnvilUseType.MERGE)
+ // Calculate rename cost
+ cost.rename = handleRename(resultItem, inventory, player)
+
+ val result =
+ DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.MERGE, cost)
+
+ return AnvilResult(result, cost)
+ }
+
+ private fun isIdentical(
+ firstEnchants: MutableMap,
+ resultEnchants: MutableMap
+ ): Boolean {
+ if (firstEnchants.size != resultEnchants.size) return false
+ for (entry in resultEnchants) {
+ if (firstEnchants.getOrDefault(entry.key, entry.value - 1) != entry.value) return false
+ }
+
+ return true
+ }
+
+ // return true if a custom recipe exist with these ingredients
+ fun testCustomRecipe(
+ view: InventoryView, //TODO use anvil view instead
+ inventory: AnvilInventory,
+ player: Player,
+ first: ItemStack, second: ItemStack?
+ ): CustomCraftResult {
+ val recipe = CustomRecipeUtil.getCustomRecipe(first, second)
+ CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}")
+ if (recipe == null) return CustomCraftResult.EMPTY
+
+ val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second)
+
+ val resultItem: ItemStack = DependencyManager.cloneItem(player, recipe.resultItem!!)
+ resultItem.amount *= amount
+
+ // Maybe add an option on custom craft to ignore/not ignore penalty ??
+ val xpCost = recipe.determineCost(amount, first, resultItem)
+
+ val cost = CustomCraftCost(xpCost)
+ // This is for displayed cost
+ cost.recipe = if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost)
+ else AnvilXpUtil.calculateLevelForXp(xpCost)
+
+ val result =
+ DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.CUSTOM_CRAFT, cost)
+ return CustomCraftResult(result, cost, amount, recipe)
+ }
+
+ fun testUnitRepair(
+ view: InventoryView, //TODO use anvil view
+ inventory: AnvilInventory,
+ player: Player,
+ first: ItemStack, second: ItemStack
+ ): UnitRepairResult {
+ val unitRepairAmount = first.getRepair(second) ?: return UnitRepairResult.EMPTY
+
+ return testUnitRepair(view, inventory, player, first, second, unitRepairAmount)
+ }
+
+ fun testUnitRepair(
+ view: InventoryView, //TODO use anvil view instead
+ inventory: AnvilInventory,
+ player: Player,
+ first: ItemStack, second: ItemStack,
+ unitRepairAmount: Double
+ ): UnitRepairResult {
+ val resultItem = DependencyManager.cloneItem(player, first)
+ val cost = AnvilCost()
+ cost.rename = handleRename(resultItem, inventory, player)
+
+ val repairAmount = resultItem.unitRepair(second.amount, unitRepairAmount)
+ if (repairAmount > 0)
+ cost.repair = repairAmount * ConfigOptions.unitRepairCost
+
+ // We do not care about right item penalty for unit repair
+ cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.UNIT_REPAIR)
+
+ // Test/stop if nothing changed.
+ if (first == resultItem) {
+ CustomAnvil.log("unit repair, But input is same as output")
+ return UnitRepairResult.EMPTY
+ }
+
+ val result =
+ DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.UNIT_REPAIR, cost)
+ return UnitRepairResult(result, cost, repairAmount)
+ }
+
+ fun testLoreEdit(
+ player: Player,
+ first: ItemStack, second: ItemStack
+ ): LoreEditResult {
+ val type = second.type
+
+ val result = if (Material.WRITABLE_BOOK == type)
+ AnvilLoreEditUtil.tryLoreEditByBook(player, first, second)
+ else if (Material.PAPER == type)
+ AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second)
+ else LoreEditResult.EMPTY
+
+ if (result.isEmpty()) return result
+
+ if (result.item!!.isAir || first == result.item) {
+ CustomAnvil.log("lore edit, But input is same as output")
+ return LoreEditResult.EMPTY
+ }
+
+ return result
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilUseType.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilUseType.kt
new file mode 100644
index 0000000..67782f3
--- /dev/null
+++ b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilUseType.kt
@@ -0,0 +1,67 @@
+package xyz.alexcrea.cuanvil.anvil
+
+import org.bukkit.Material
+import xyz.alexcrea.cuanvil.config.WorkPenaltyType
+import xyz.alexcrea.cuanvil.util.anvil.AnvilUseTypeUtil
+
+enum class AnvilUseType(
+ val typeName: String, val path: String,
+ val defaultPenalty: WorkPenaltyType.WorkPenaltyPart,
+ val displayName: String, val displayMat: Material
+) {
+
+ RENAME_ONLY(
+ "rename_only",
+ WorkPenaltyType.WorkPenaltyPart(false, true),
+ "Rename Only", Material.NAME_TAG
+ ),
+ MERGE(
+ "merge",
+ WorkPenaltyType.WorkPenaltyPart(true, true),
+ "Merge", Material.ANVIL
+ ),
+ UNIT_REPAIR(
+ "unit_repair",
+ WorkPenaltyType.WorkPenaltyPart(true, true),
+ "Unit Repair", Material.DIAMOND
+ ),
+ CUSTOM_CRAFT(
+ "custom_craft",
+ WorkPenaltyType.WorkPenaltyPart(false, false),
+ "Custom Craft", Material.CRAFTING_TABLE
+ ),
+ LORE_EDIT_BOOK_APPEND(
+ "lore_edit_book_append", "lore_edit.book_and_quil.append",
+ WorkPenaltyType.WorkPenaltyPart(false, false),
+ "Book Add", Material.WRITABLE_BOOK
+ ),
+ LORE_EDIT_BOOK_REMOVE(
+ "lore_edit_book_remove", "lore_edit.book_and_quil.remove",
+ WorkPenaltyType.WorkPenaltyPart(false, false),
+ "Book Remove", Material.WRITABLE_BOOK
+ ),
+ LORE_EDIT_PAPER_APPEND(
+ "lore_edit_paper_append", "lore_edit.paper.append_line",
+ WorkPenaltyType.WorkPenaltyPart(false, false),
+ "Paper Add", Material.WRITABLE_BOOK
+ ),
+ LORE_EDIT_PAPER_REMOVE(
+ "lore_edit_paper_remove", "lore_edit.paper.remove_line",
+ WorkPenaltyType.WorkPenaltyPart(false, false),
+ "Paper Remove", Material.WRITABLE_BOOK
+ ),
+ ;
+
+ constructor(
+ typeName: String,
+ defaultPenalty: WorkPenaltyType.WorkPenaltyPart,
+ displayName: String, displayMat: Material
+ ) :
+ this(
+ typeName,
+ AnvilUseTypeUtil.defaultPath(typeName), // stupid util class
+ defaultPenalty,
+ displayName, displayMat
+ )
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/command/CASubCommand.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/command/CASubCommand.kt
new file mode 100644
index 0000000..4c71c68
--- /dev/null
+++ b/src/main/kotlin/xyz/alexcrea/cuanvil/command/CASubCommand.kt
@@ -0,0 +1,46 @@
+package xyz.alexcrea.cuanvil.command
+
+import org.bukkit.ChatColor
+import org.bukkit.command.Command
+import org.bukkit.command.CommandExecutor
+import org.bukkit.command.CommandSender
+
+abstract class CASubCommand: CommandExecutor {
+
+ private var alreadySaid = false;
+ override fun onCommand(
+ sender: CommandSender,
+ cmd: Command,
+ cmdstr: String,
+ args: Array
+ ): Boolean {
+ if(!alreadySaid){
+ sender.sendMessage(ChatColor.RED.toString() +
+ "Please not that this command will be replaced as a subcommand of `/customanvil` or `/ca`")
+ alreadySaid = true
+ }
+
+ return executeCommand(sender, cmd, cmdstr, args)
+ }
+
+ abstract fun executeCommand(
+ sender: CommandSender,
+ cmd: Command,
+ cmdstr: String,
+ args: Array): Boolean
+
+ open fun allowed(sender: CommandSender): Boolean {
+ return true
+ }
+
+ open fun tabCompleter(
+ sender: CommandSender,
+ args: Array,
+ list: MutableList) {
+ }
+
+ open fun description(): String {
+ return "no description"
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/command/CustomAnvilCommand.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/command/CustomAnvilCommand.kt
new file mode 100644
index 0000000..e5e689e
--- /dev/null
+++ b/src/main/kotlin/xyz/alexcrea/cuanvil/command/CustomAnvilCommand.kt
@@ -0,0 +1,96 @@
+package xyz.alexcrea.cuanvil.command
+
+import com.google.common.collect.ImmutableMap
+import io.delilaheve.CustomAnvil
+import org.bukkit.command.Command
+import org.bukkit.command.CommandExecutor
+import org.bukkit.command.CommandSender
+import org.bukkit.command.TabCompleter
+import xyz.alexcrea.cuanvil.util.MetricsUtil
+import java.util.ArrayList
+
+class CustomAnvilCommand(plugin: CustomAnvil) : CommandExecutor, TabCompleter {
+
+ // Name of the generic command
+ companion object {
+ private const val genericCommandName = "customanvil"
+ }
+
+ private val editConfigCommand = EditConfigExecutor()
+ private val helpCommand = HelpExecutor()
+ private val commands = ImmutableMap.of(
+ "gui", editConfigCommand,
+ "reload", ReloadExecutor(),
+ "diagnostic", DiagnosticExecutor(),
+ "help", helpCommand,
+ )
+
+ init {
+ val self = plugin.getCommand(genericCommandName)!!
+ self.setExecutor(this)
+ self.tabCompleter = this
+
+ helpCommand.commands = commands
+ }
+
+ override fun onCommand(
+ sender: CommandSender,
+ cmd: Command,
+ cmdstr: String,
+ args: Array
+ ): Boolean {
+ // Find sub command to execute based on the provided command name
+ val subcmd: CASubCommand?
+ val newargs: Array
+ if(args.isEmpty()) {
+ subcmd = editConfigCommand
+ newargs = args
+ }else {
+ subcmd = commands[args[0].lowercase()]
+ newargs = args.copyOfRange(1, args.size)
+ }
+
+ if(subcmd == null || !subcmd.allowed(sender)) {
+ sender.sendMessage("Invalid subcommand. run `$cmdstr help` to see available commands")
+ return true
+ }
+
+ try {
+ return subcmd.executeCommand(sender, cmd, cmdstr, newargs)
+ } catch (e: Throwable) {
+ MetricsUtil.trackError(e)
+ sender.sendMessage("§cError running this command")
+ return false
+ }
+ }
+
+ override fun onTabComplete(
+ sender: CommandSender,
+ cmd: Command,
+ cmdstr: String,
+ args: Array
+ ): MutableList