Feature/datapack (#58)

Add support for enchantment of the-bracken-pack datapack
also do some minor enchantment fix
This commit is contained in:
alexcrea 2025-05-06 12:41:39 +02:00 committed by GitHub
commit 914cd0b949
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 849 additions and 124 deletions

View file

@ -16,7 +16,7 @@ plugins {
} }
group = "xyz.alexcrea" group = "xyz.alexcrea"
version = "1.10.1" version = "1.11.0"
repositories { repositories {
// EcoEnchants // EcoEnchants

View file

@ -0,0 +1,30 @@
package xyz.alexcrea.cuanvil.dependency.datapack
import io.papermc.paper.datapack.Datapack
import org.bukkit.Bukkit
import java.util.*
object DataPackTester {
val legacyNames: List<String>
get() = Bukkit.getDataPackManager().dataPacks
.stream().filter { obj -> obj.isEnabled }
.map { pack -> pack.key.key }
.toList()
val enabledPacks: List<String>
get() {
try {
// will throw error if do not exist
Bukkit::class.java.getDeclaredMethod("getDatapackManager")
return Bukkit.getDatapackManager().enabledPacks
.stream().map { obj: Datapack -> obj.name }
.toList()
} catch (e: NoSuchMethodException) {
return legacyNames
} catch (e: Exception){
// Assume cause UnimplementedOperationException on mock server
return Collections.emptyList()
}
}
}

View file

@ -105,7 +105,9 @@ public class MaterialGroupApi {
if (group instanceof IncludeGroup includeGroup) { if (group instanceof IncludeGroup includeGroup) {
changed = writeKnownGroup("include", includeGroup); changed = writeKnownGroup("include", includeGroup);
} else if (group instanceof ExcludeGroup excludeGroup) { } else if (group instanceof ExcludeGroup excludeGroup) {
changed = writeKnownGroup("exclude", 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 { } else {
changed = writeUnknownGroup(group); changed = writeUnknownGroup(group);
} }
@ -124,13 +126,24 @@ public class MaterialGroupApi {
Set<Material> materialSet = group.getNonGroupInheritedMaterials(); Set<Material> materialSet = group.getNonGroupInheritedMaterials();
Set<AbstractMaterialGroup> groupSet = group.getGroups(); Set<AbstractMaterialGroup> groupSet = group.getGroups();
boolean empty = true;
if (!materialSet.isEmpty()) { if (!materialSet.isEmpty()) {
config.set(basePath + ItemGroupManager.MATERIAL_LIST_PATH, materialSetToStringList(materialSet)); config.set(basePath + ItemGroupManager.MATERIAL_LIST_PATH, materialSetToStringList(materialSet));
empty = false;
} else {
config.set(basePath + ItemGroupManager.MATERIAL_LIST_PATH, null);
} }
if (!groupSet.isEmpty()) { if (!groupSet.isEmpty()) {
config.set(basePath + ItemGroupManager.GROUP_LIST_PATH, materialGroupSetToStringList(groupSet)); 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;
} }
if (!config.isConfigurationSection(group.getName())) return false;
config.set(basePath + ItemGroupManager.GROUP_TYPE_PATH, groupType); config.set(basePath + ItemGroupManager.GROUP_TYPE_PATH, groupType);
return true; return true;

View file

@ -76,12 +76,12 @@ public class CAEnchantmentRegistry {
*/ */
public boolean register(@NotNull CAEnchantment enchantment) { public boolean register(@NotNull CAEnchantment enchantment) {
if (byKeyMap.containsKey(enchantment.getKey())) { if (byKeyMap.containsKey(enchantment.getKey())) {
if (!enchantment.equals(byKeyMap.get(enchantment.getKey()))) { if (Objects.equals(enchantment, byKeyMap.get(enchantment.getKey()))) {
// We are trying to register the exact same enchantment. so we just skip it. // We are trying to register the exact same enchantment. so we just skip it.
return false; return false;
} }
if(ConfigHolder.DEFAULT_CONFIG.getConfig().getBoolean("caution_secret_do_not_log_duplicated_registered_key", false)){ if (ConfigHolder.DEFAULT_CONFIG.getConfig().getBoolean("caution_secret_do_not_log_duplicated_registered_key", false)) {
return false; return false;
} }

View file

@ -18,6 +18,7 @@ import java.lang.reflect.Method;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.logging.Level; import java.util.logging.Level;
/** /**
@ -25,17 +26,17 @@ import java.util.logging.Level;
*/ */
public class CABukkitEnchantment extends CAEnchantmentBase { public class CABukkitEnchantment extends CAEnchantmentBase {
private final @NotNull Enchantment enchantment; public final @NotNull Enchantment bukkit;
public CABukkitEnchantment(@NotNull Enchantment enchantment, @Nullable EnchantmentRarity rarity){ public CABukkitEnchantment(@NotNull Enchantment bukkit, @Nullable EnchantmentRarity rarity){
super(enchantment.getKey(), super(bukkit.getKey(),
rarity, rarity,
enchantment.getMaxLevel()); bukkit.getMaxLevel());
this.enchantment = enchantment; this.bukkit = bukkit;
} }
public CABukkitEnchantment(@NotNull Enchantment enchantment){ public CABukkitEnchantment(@NotNull Enchantment bukkit){
this(enchantment, getRarity(enchantment)); this(bukkit, getRarity(bukkit));
} }
@Override @Override
@ -51,9 +52,9 @@ public class CABukkitEnchantment extends CAEnchantmentBase {
@Override @Override
public int getLevel(@NotNull ItemStack item, @NotNull ItemMeta meta) { public int getLevel(@NotNull ItemStack item, @NotNull ItemMeta meta) {
if (ItemUtil.INSTANCE.isEnchantedBook(item)) { if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
return ((EnchantmentStorageMeta)meta).getStoredEnchantLevel(this.enchantment); return ((EnchantmentStorageMeta)meta).getStoredEnchantLevel(this.bukkit);
} else { } else {
return meta.getEnchantLevel(this.enchantment); return meta.getEnchantLevel(this.bukkit);
} }
} }
@ -62,9 +63,9 @@ public class CABukkitEnchantment extends CAEnchantmentBase {
if (ItemUtil.INSTANCE.isEnchantedBook(item)) { if (ItemUtil.INSTANCE.isEnchantedBook(item)) {
EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta)meta); EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta)meta);
return bookMeta.getStoredEnchants().containsKey(this.enchantment); return bookMeta.getStoredEnchants().containsKey(this.bukkit);
}else{ }else{
return item.containsEnchantment(this.enchantment); return item.containsEnchantment(this.bukkit);
} }
} }
@ -74,10 +75,10 @@ public class CABukkitEnchantment extends CAEnchantmentBase {
EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta)item.getItemMeta()); EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta)item.getItemMeta());
assert bookMeta != null; assert bookMeta != null;
bookMeta.addStoredEnchant(this.enchantment, level, true); bookMeta.addStoredEnchant(this.bukkit, level, true);
item.setItemMeta(bookMeta); item.setItemMeta(bookMeta);
} else { } else {
item.addUnsafeEnchantment(this.enchantment, level); item.addUnsafeEnchantment(this.bukkit, level);
} }
} }
@ -88,10 +89,10 @@ public class CABukkitEnchantment extends CAEnchantmentBase {
EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta)item.getItemMeta()); EnchantmentStorageMeta bookMeta = ((EnchantmentStorageMeta)item.getItemMeta());
assert bookMeta != null; assert bookMeta != null;
bookMeta.removeStoredEnchant(this.enchantment); bookMeta.removeStoredEnchant(this.bukkit);
item.setItemMeta(bookMeta); item.setItemMeta(bookMeta);
}else{ }else{
item.removeEnchantment(this.enchantment); item.removeEnchantment(this.bukkit);
} }
} }
@ -107,7 +108,7 @@ public class CABukkitEnchantment extends CAEnchantmentBase {
@NotNull @NotNull
protected Enchantment getEnchant() { protected Enchantment getEnchant() {
return this.enchantment; return this.bukkit;
} }
private static Method getAnvilCostMethod; private static Method getAnvilCostMethod;
@ -163,7 +164,7 @@ public class CABukkitEnchantment extends CAEnchantmentBase {
return false; return false;
} }
return this.enchantment.equals(other.getEnchant()); return Objects.equals(this.bukkit, other.getEnchant());
} }
} }

View file

@ -0,0 +1,35 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import org.bukkit.Material;
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<CAEnchantment, Integer> enchantments, @NotNull Material itemMat) {
return !enchantments.isEmpty() && !(enchantments.size() == 1 && enchantments.containsKey(this));
}
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat, @NotNull ItemStack item) {
return false;
}
}

View file

@ -10,18 +10,18 @@ import java.util.List;
public class UpdateUtils { public class UpdateUtils {
public static final String MINECRAFT_VERSION_PATH = "lowMinecraftVersion"; public static final String MINECRAFT_VERSION_PATH = "lowMinecraftVersion";
public static Version currentMinecraftVersion(){ public static Version currentMinecraftVersion() {
String versionString = Bukkit.getServer().getBukkitVersion().split("-")[0]; String versionString = Bukkit.getServer().getBukkitVersion().split("-")[0];
return Version.fromString(versionString); return Version.fromString(versionString);
} }
@Deprecated @Deprecated
public static int[] currentMinecraftVersionArray(){ public static int[] currentMinecraftVersionArray() {
String versionString = Bukkit.getServer().getBukkitVersion().split("-")[0]; String versionString = Bukkit.getServer().getBukkitVersion().split("-")[0];
return UpdateUtils.readVersionFromString(versionString); return UpdateUtils.readVersionFromString(versionString);
} }
public static int[] readVersionFromString(String versionString){ public static int[] readVersionFromString(String versionString) {
String[] partialVersion = versionString.split("\\."); String[] partialVersion = versionString.split("\\.");
int[] versionParts = new int[]{0, 0, 0}; int[] versionParts = new int[]{0, 0, 0};
@ -31,11 +31,22 @@ public class UpdateUtils {
return versionParts; return versionParts;
} }
public static void addToStringList(FileConfiguration config, String path, String... toAdd){ public static void addToStringList(FileConfiguration config, String path, String... toAdd) {
List<String> groups = new ArrayList<>(config.getStringList(path)); List<String> groups = new ArrayList<>(config.getStringList(path));
groups.addAll(Arrays.asList(toAdd)); groups.addAll(Arrays.asList(toAdd));
config.set(path, groups); config.set(path, groups);
} }
public static void addAbsentToList(FileConfiguration config, String path, String... toAdd) {
List<String> groups = new ArrayList<>(config.getStringList(path));
for (String val : toAdd) {
if (groups.contains(val)) continue;
groups.add(val);
}
config.set(path, groups);
}
} }

View file

@ -4,7 +4,7 @@ import io.delilaheve.CustomAnvil;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import xyz.alexcrea.cuanvil.config.ConfigHolder; import xyz.alexcrea.cuanvil.config.ConfigHolder;
import static xyz.alexcrea.cuanvil.update.UpdateUtils.addToStringList; import static xyz.alexcrea.cuanvil.update.UpdateUtils.addAbsentToList;
// This is a temporary class that aim to handle 1.21 update. // This is a temporary class that aim to handle 1.21 update.
// It will be replaced by a better system later. // It will be replaced by a better system later.
@ -40,28 +40,27 @@ public class Update_1_21 {
// Add mace to groups // Add mace to groups
groupConfig.set("mace.type", "include"); groupConfig.set("mace.type", "include");
addToStringList(groupConfig, "mace.items", "mace"); addAbsentToList(groupConfig, "mace.items", "mace");
addToStringList(groupConfig, "can_unbreak.groups", "mace"); addAbsentToList(groupConfig, "can_unbreak.groups", "mace");
// Add new enchant conflicts // Add new enchant conflicts
addToStringList(conflictConfig, "restriction_density.enchantments", "minecraft:density"); addAbsentToList(conflictConfig, "restriction_density.enchantments", "minecraft:density");
addToStringList(conflictConfig, "restriction_density.notAffectedGroups", "mace", "enchanted_book"); addAbsentToList(conflictConfig, "restriction_density.notAffectedGroups", "mace", "enchanted_book");
addToStringList(conflictConfig, "restriction_breach.enchantments", "minecraft:breach"); addAbsentToList(conflictConfig, "restriction_breach.enchantments", "minecraft:breach");
addToStringList(conflictConfig, "restriction_breach.notAffectedGroups", "mace", "enchanted_book"); addAbsentToList(conflictConfig, "restriction_breach.notAffectedGroups", "mace", "enchanted_book");
addToStringList(conflictConfig, "restriction_wind_burst.enchantments", "minecraft:wind_burst"); addAbsentToList(conflictConfig, "restriction_wind_burst.enchantments", "minecraft:wind_burst");
addToStringList(conflictConfig, "restriction_wind_burst.notAffectedGroups", "mace", "enchanted_book"); addAbsentToList(conflictConfig, "restriction_wind_burst.notAffectedGroups", "mace", "enchanted_book");
// Add mace to conflicts // Add mace to conflicts
addToStringList(conflictConfig, "restriction_fire_aspect.notAffectedGroups", "mace"); addAbsentToList(conflictConfig, "restriction_fire_aspect.notAffectedGroups", "mace");
addToStringList(conflictConfig, "restriction_smite.notAffectedGroups", "mace"); addAbsentToList(conflictConfig, "restriction_smite.notAffectedGroups", "mace");
addToStringList(conflictConfig, "restriction_bane_of_arthropods.notAffectedGroups", "mace"); addAbsentToList(conflictConfig, "restriction_bane_of_arthropods.notAffectedGroups", "mace");
addToStringList(conflictConfig, "mace_enchant_conflict.enchantments", addAbsentToList(conflictConfig, "sword_enchant_conflict.enchantments",
"minecraft:density", "minecraft:breach", "minecraft:smite", "minecraft:bane_of_arthropods"); "minecraft:density", "minecraft:breach");
conflictConfig.set("mace_enchant_conflict.maxEnchantmentBeforeConflict", 1);
// Add level limit // Add level limit
baseConfig.set("enchant_limits.minecraft:density", 5); baseConfig.set("enchant_limits.minecraft:density", 5);

View file

@ -1,6 +1,7 @@
package xyz.alexcrea.cuanvil.update; package xyz.alexcrea.cuanvil.update;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public record Version(int major, int minor, int patch) { public record Version(int major, int minor, int patch) {
@ -11,7 +12,9 @@ public record Version(int major, int minor, int patch) {
this(major, 0, 0); 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("\\."); String[] partialVersion = versionString.split("\\.");
int[] versionParts = new int[]{0, 0, 0}; int[] versionParts = new int[]{0, 0, 0};
@ -45,4 +48,8 @@ public record Version(int major, int minor, int patch) {
this.patch <= other.patch))); this.patch <= other.patch)));
} }
@Override
public String toString() {
return major + "." + minor + "." + patch;
}
} }

View file

@ -0,0 +1,130 @@
package xyz.alexcrea.cuanvil.update.plugin;
import org.bukkit.Material;
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.List;
import java.util.Set;
import static xyz.alexcrea.cuanvil.update.UpdateUtils.addAbsentToList;
public class PUpdate_1_11_0 {
private static final List<String> mace_expected = List.of(
"density",
"breach",
"smite",
"bane_of_arthropods"
);
private static final List<String> 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<ConfigHolder> 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);
group.addAll(toolMats);
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<Material> mats = List.of(toolMats);
Set<Material> matSet = include.getNonGroupInheritedMaterials();
if (!matSet.containsAll(mats)) return;
mats.forEach(matSet::remove);
tools.addToPolicy(group);
MaterialGroupApi.writeMaterialGroup(tools);
}
private static void handleMaceMigration(@Nonnull Set<ConfigHolder> 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<String> 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);
}
}

View file

@ -1,13 +1,12 @@
package xyz.alexcrea.cuanvil.update.plugin; package xyz.alexcrea.cuanvil.update.plugin;
import io.delilaheve.CustomAnvil;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import xyz.alexcrea.cuanvil.config.ConfigHolder; import xyz.alexcrea.cuanvil.config.ConfigHolder;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.Set; 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 { public class PUpdate_1_6_2 {
@ -30,7 +29,7 @@ public class PUpdate_1_6_2 {
} }
if(!contained){ if(!contained){
addToStringList(config, path, "enchanted_book"); addAbsentToList(config, path, "enchanted_book");
conflictUpdated = true; conflictUpdated = true;
} }
} }

View file

@ -20,18 +20,21 @@ public class PluginUpdates {
if (new Version(1, 6, 2).greaterThan(current)) { if (new Version(1, 6, 2).greaterThan(current)) {
PUpdate_1_6_2.handleUpdate(toSave); PUpdate_1_6_2.handleUpdate(toSave);
// We assume 1.6.7 will run. TODO a better system instead of that I guess // We assume 1.6.7 will run. TODO a better system instead of that I guess
} }
if (new Version(1, 6, 7).greaterThan(current)) { if (new Version(1, 6, 7).greaterThan(current)) {
PUpdate_1_6_7.handleUpdate(toSave); PUpdate_1_6_7.handleUpdate(toSave);
// We assume 1.8.0 will run. // We assume 1.8.0 will run.
} }
if (new Version(1, 8, 0).greaterThan(current)) { if (new Version(1, 8, 0).greaterThan(current)) {
PUpdate_1_8_0.handleUpdate(toSave); PUpdate_1_8_0.handleUpdate(toSave);
// We assume 1.11.0 will run.
}
finishConfiguration("1.8.0", toSave); if (new Version(1, 11, 0).greaterThan(current)) {
PUpdate_1_11_0.handleUpdate(toSave);
finishConfiguration("1.11.0", toSave);
} }
} }

View file

@ -2,6 +2,7 @@ package io.delilaheve.util
import io.delilaheve.CustomAnvil import io.delilaheve.CustomAnvil
import io.delilaheve.util.EnchantmentUtil.enchantmentName import io.delilaheve.util.EnchantmentUtil.enchantmentName
import org.bukkit.NamespacedKey
import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.config.WorkPenaltyType import xyz.alexcrea.cuanvil.config.WorkPenaltyType
import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart
@ -51,6 +52,8 @@ object ConfigOptions {
const val DISABLE_MERGE_OVER_ROOT = "disable-merge-over" const val DISABLE_MERGE_OVER_ROOT = "disable-merge-over"
const val IMMUTABLE_ENCHANTMENT_LIST = "immutable_enchantments"
// Keys for specific enchantment values // Keys for specific enchantment values
private const val KEY_BOOK = "book" private const val KEY_BOOK = "book"
private const val KEY_ITEM = "item" private const val KEY_ITEM = "item"
@ -478,4 +481,17 @@ object ConfigOptions {
.takeIf { it in ENCHANT_LIMIT_RANGE } .takeIf { it in ENCHANT_LIMIT_RANGE }
} }
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
}
} }

View file

@ -9,6 +9,7 @@ import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.datapack.DataPackDependency
import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester
import xyz.alexcrea.cuanvil.dependency.gui.GuiTesterSelector import xyz.alexcrea.cuanvil.dependency.gui.GuiTesterSelector
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager import xyz.alexcrea.cuanvil.dependency.packet.PacketManager
@ -78,12 +79,13 @@ object DependencyManager {
havenBagsCompatibility = HavenBagsDependency() havenBagsCompatibility = HavenBagsDependency()
havenBagsCompatibility!!.redirectListeners() havenBagsCompatibility!!.redirectListeners()
} }
} }
fun handleCompatibilityConfig() { fun handleCompatibilityConfig() {
enchantmentSquaredCompatibility?.registerPluginConfiguration() enchantmentSquaredCompatibility?.registerPluginConfiguration()
// datapacks
DataPackDependency.handleDatapackConfigs()
} }
fun registerEnchantments() { fun registerEnchantments() {
@ -100,6 +102,35 @@ object DependencyManager {
// Then handle plugin reload // Then handle plugin reload
ecoEnchantCompatibility?.handleConfigReload() ecoEnchantCompatibility?.handleConfigReload()
} }
// Return true if should bypass (either by a dependency or error)
// called before immutability test
fun earlyTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
try {
return earlyUnsafeTryEventPreAnvilBypass(event, player)
} catch (e: Exception) {
CustomAnvil.instance.logger.log(
Level.SEVERE,
"Error while trying to handle custom anvil supported plugin: ",
e
)
// Just in case to avoid illegal items
event.inventory.setItem(ANVIL_OUTPUT_SLOT, null)
// Finally, warn the player, maybe a lot of time but better warn than do nothing
event.view.player.sendMessage(ChatColor.RED.toString() + "Error while handling the anvil.")
return true
}
}
private fun earlyUnsafeTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
var bypass = false
// Test if the inventory is a gui(version specific)
if (externGuiTester?.testIfGui(event.view) == true) bypass = true
return bypass
}
// Return true if should bypass (either by a dependency or error) // Return true if should bypass (either by a dependency or error)
fun tryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean { fun tryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
@ -133,10 +164,6 @@ object DependencyManager {
// Test excellent enchantments used prepare anvil // Test excellent enchantments used prepare anvil
if (!bypass && (excellentEnchantsCompatibility?.testPrepareAnvil(event) == true)) bypass = true if (!bypass && (excellentEnchantsCompatibility?.testPrepareAnvil(event) == true)) bypass = true
// Test if the inventory is a gui(version specific)
if (!bypass && (externGuiTester?.testIfGui(event.view) == true)) bypass = true
return bypass return bypass
} }

View file

@ -101,19 +101,6 @@ class EnchantmentSquaredDependency(private val enchantmentSquaredPlugin: Plugin)
private fun writeMissingGroups(){ private fun writeMissingGroups(){
// Write group that do not exist on custom anvil. // Write group that do not exist on custom anvil.
// (Tools group regroup most of the tool items. I did not create a seperated group for theses)
val pickaxes = IncludeGroup("pickaxes")
pickaxes.addAll(Material.WOODEN_PICKAXE, Material.STONE_PICKAXE, Material.IRON_PICKAXE, Material.DIAMOND_PICKAXE, Material.GOLDEN_PICKAXE, Material.NETHERITE_PICKAXE)
MaterialGroupApi.addMaterialGroup(pickaxes)
val shovels = IncludeGroup("shovels")
shovels.addAll(Material.WOODEN_SHOVEL, Material.STONE_SHOVEL, Material.IRON_SHOVEL, Material.DIAMOND_SHOVEL, Material.GOLDEN_SHOVEL, Material.NETHERITE_SHOVEL)
MaterialGroupApi.addMaterialGroup(shovels)
val hoes = IncludeGroup("hoes")
hoes.addAll(Material.WOODEN_HOE, Material.STONE_HOE, Material.IRON_HOE, Material.DIAMOND_HOE, Material.GOLDEN_HOE, Material.NETHERITE_HOE)
MaterialGroupApi.addMaterialGroup(hoes)
val shield = IncludeGroup("shield") val shield = IncludeGroup("shield")
shield.addToPolicy(Material.SHIELD) shield.addToPolicy(Material.SHIELD)
MaterialGroupApi.addMaterialGroup(shield) MaterialGroupApi.addMaterialGroup(shield)
@ -125,7 +112,6 @@ class EnchantmentSquaredDependency(private val enchantmentSquaredPlugin: Plugin)
val trinkets = IncludeGroup("trinkets") val trinkets = IncludeGroup("trinkets")
trinkets.addToPolicy(Material.ROTTEN_FLESH) trinkets.addToPolicy(Material.ROTTEN_FLESH)
MaterialGroupApi.addMaterialGroup(trinkets) MaterialGroupApi.addMaterialGroup(trinkets)
} }
private fun writeMaterialRestriction(esEnchantments: List<CAEnchantSquaredEnchantment>){ private fun writeMaterialRestriction(esEnchantments: List<CAEnchantSquaredEnchantment>){

View file

@ -0,0 +1,290 @@
package xyz.alexcrea.cuanvil.dependency.datapack
import io.delilaheve.CustomAnvil
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.configuration.file.FileConfiguration
import org.bukkit.configuration.file.YamlConfiguration
import xyz.alexcrea.cuanvil.api.ConflictBuilder
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.api.MaterialGroupApi
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.enchant.wrapped.CABukkitEnchantment
import xyz.alexcrea.cuanvil.enchant.wrapped.CAIncompatibleAllEnchant
import xyz.alexcrea.cuanvil.group.IncludeGroup
import xyz.alexcrea.cuanvil.update.UpdateUtils
import xyz.alexcrea.cuanvil.update.Version
import java.io.InputStreamReader
object DataPackDependency {
private val START_DETECT_VERSION = Version(1, 19, 0)
/**
* Map of the latest CustomAnvil update related to the pack
*/
private val LASTEST_VERSION = mapOf(
Pair("bracken", Version(1, 11, 0))
)
val enabledDatapacks: List<String>
get() {
val version: Version = UpdateUtils.currentMinecraftVersion()
if (version.lesserThan(START_DETECT_VERSION)) return emptyList()
return DataPackTester.enabledPacks
}
fun handleDatapackConfigs() {
val enabledDatapack = enabledDatapacks
for (packName in enabledDatapack) {
// Handling of pack name is horrible: it is based on file name
// So if someone rename a datapack it will make me sad
if(!packName.startsWith("file/")) continue
if (packName.contains("bp_post_scarcity", ignoreCase = true)
|| packName.contains("bracken", ignoreCase = true)) {
handlePack("bracken")
continue
}
}
}
private fun handlePack(pack: String){
CustomAnvil.instance.logger.info("trying to handle datapack $pack")
handlePackInitialConfig(pack)
writeDefaultByNamespace(pack)
CustomAnvil.instance.logger.info("configuration done for $pack")
}
private fun handlePackInitialConfig(pack: String) {
val defConfig = ConfigHolder.DEFAULT_CONFIG
val version = LASTEST_VERSION[pack]
val currentVersion = Version.fromString(defConfig.config.getString("datapack.$pack"))
if (currentVersion.greaterEqual(version!!)) {
handleEnchantAllConflict(pack)
return
}
// Add pack value or do update from previous version
// note: update thingy is not yet implemented
configureDatapack(pack)
// Finally, set current pack version to config
defConfig.config.set("datapack.$pack", version.toString())
defConfig.saveToDisk(true)
}
private fun configureDatapack(pack: String) {
val itemGroups = javaClass.getResource("/datapack/$pack/item_groups.yml")
val itemConflict = javaClass.getResource("/datapack/$pack/item_conflict.yml")
val enchantConflict = javaClass.getResource("/datapack/$pack/enchant_conflict.yml")
if (itemGroups != null) {
val reader = InputStreamReader(itemGroups.openStream())
val yml = YamlConfiguration.loadConfiguration(reader)
handleItemGroups(yml)
}
val newConflictList = ArrayList<ConflictBuilder>()
var needSave = false
if (itemConflict != null) {
val reader = InputStreamReader(itemConflict.openStream())
val yml = YamlConfiguration.loadConfiguration(reader)
addItemConflicts(yml, newConflictList)
}
if (enchantConflict != null) {
val reader = InputStreamReader(enchantConflict.openStream())
val yml = YamlConfiguration.loadConfiguration(reader)
needSave = addEnchantConflict(yml, newConflictList)
}
for (conflict in newConflictList) {
needSave = !conflict.registerIfAbsent() && needSave
}
if (needSave) {
ConfigHolder.CONFLICT_HOLDER.saveToDisk(true)
}
}
// Order matter for this file
// Could rewrite to not matter but not really important, so I keep it like that
private fun handleItemGroups(yml: YamlConfiguration) {
for (groupName in yml.getKeys(false)) {
val section = yml.getConfigurationSection(groupName) ?: continue
var group = MaterialGroupApi.getGroup(groupName)
val exist = group != null
if (group == null) group = IncludeGroup(groupName)
for (name in section.getStringList("items")) {
val mat = Material.getMaterial(name.uppercase())
if (mat == null) {
CustomAnvil.instance.logger.warning("Could not find material $name for item group $groupName")
continue
}
group.addToPolicy(mat)
}
for (name in section.getStringList("groups")) {
val otherGroup = MaterialGroupApi.getGroup(name)
if (otherGroup == null) {
CustomAnvil.instance.logger.warning("Could not find sub group $name for group $groupName")
continue
}
group.addToPolicy(otherGroup)
}
group.updateMaterials()
if (exist) {
MaterialGroupApi.writeMaterialGroup(group)
} else {
MaterialGroupApi.addMaterialGroup(group, true)
}
}
}
private fun addItemConflicts(yml: FileConfiguration, conflictList: MutableList<ConflictBuilder>) {
for (ench in yml.getKeys(false)) {
val groups = yml.getStringList(ench)
val conflict = ConflictBuilder(
"restriction_${ench.replace(":", "_")}",
CustomAnvil.instance
)
conflict.addEnchantment(NamespacedKey.fromString(ench)!!)
for (group in groups) {
conflict.addExcludedGroup(group)
}
conflict.addExcludedGroup("enchanted_book")
conflictList.add(conflict)
}
}
private fun addEnchantConflict(yml: YamlConfiguration, conflictList: MutableList<ConflictBuilder>): Boolean {
var needSave = false
val conflicts = HashMap<String, ConflictBuilder>()
for (ench in yml.getKeys(false)) {
val groups = yml.getStringList(ench)
for (group in groups) {
if (group.startsWith('#')) {
needSave = joinGroup(conflicts, group.substring(1), ench) || needSave
} else {
createConflict(conflictList, ench, group)
}
}
}
conflictList.addAll(conflicts.values)
return needSave
}
private fun createConflict(conflictList: MutableList<ConflictBuilder>, ench: String, other: String) {
val conflict = ConflictBuilder(
"conflict_" +
"${ench.replace(":", "_")}_" +
other.replace(":", "_"),
CustomAnvil.instance
)
conflict.addEnchantment(NamespacedKey.fromString(ench)!!)
conflict.addEnchantment(NamespacedKey.fromString(other)!!)
conflict.setMaxBeforeConflict(1)
conflictList.add(conflict)
}
private fun setEnchantAsAll(ench: String) {
// We assume current is not null and of type CABukkitEnchantment
val current = EnchantmentApi.getByKey(NamespacedKey.fromString(ench)!!) as CABukkitEnchantment
// We need to replace current wrapped enchantment with the all conflict wrapper
EnchantmentApi.unregisterEnchantment(current)
EnchantmentApi.registerEnchantment(CAIncompatibleAllEnchant(current.bukkit, current.defaultRarity()))
}
private fun joinGroup(conflicts: HashMap<String, ConflictBuilder>, group: String, ench: String): Boolean {
if ("all".equals(group, ignoreCase = true)) {
setEnchantAsAll(ench)
return false
} else {
val config = ConfigHolder.CONFLICT_HOLDER.config
// If conflict do not yet exist
if (!config.isConfigurationSection(group)) {
val conflict = conflicts.getOrPut(group) {
val conflict = ConflictBuilder(group, CustomAnvil.instance)
conflict.setMaxBeforeConflict(1)
conflict
}
conflict.addEnchantment(NamespacedKey.fromString(ench)!!)
return false
}
// Find current conflict
val manager = ConfigHolder.CONFLICT_HOLDER.conflictManager
// This assumes that:
// - the conflict existing in the config exist in the runtime config (as configuration section exist)
// - the enchantment exist and is provided correctly
val conflict = manager.conflictList.find {
it.name.equals(group, ignoreCase = true)
}
if(conflict == null) {
// This should not happen as configuration section
CustomAnvil.instance.logger.severe("Could not find $group while its configuration section exist... this should NOT happen")
return false
}
val key = NamespacedKey.fromString(ench)!!
val enchant = EnchantmentApi.getByKey(key)
if (enchant == null){
CustomAnvil.instance.logger.severe("Could not find enchantment $ench while configuring pack a datapack")
return false
}
conflict.addEnchantment(enchant)
UpdateUtils.addAbsentToList(config, "$group.enchantments", ench)
return true
}
}
private fun handleEnchantAllConflict(pack: String) {
val enchantConflict = javaClass.getResource("/datapack/$pack/enchant_conflict.yml") ?: return
val reader = InputStreamReader(enchantConflict.openStream())
val yml = YamlConfiguration.loadConfiguration(reader)
for (ench in yml.getKeys(false)) {
val groups = yml.getStringList(ench)
if (groups.contains("#all")) {
setEnchantAsAll(ench)
}
}
}
private fun writeDefaultByNamespace(namespace: String) {
for (enchantment in EnchantmentApi.getRegisteredEnchantments().values) {
if(!enchantment.key.namespace.equals(namespace, ignoreCase = true)) continue
CustomAnvil.log("Writing default for $enchantment")
EnchantmentApi.writeDefaultConfig(enchantment, false)
}
}
}

View file

@ -31,8 +31,9 @@ class EnchantConflictManager {
// 1.20.5 compatibility TODO better update system // 1.20.5 compatibility TODO better update system
private val SWEEPING_EDGE_ENCHANT = Collections.singletonList<CAEnchantment>( private val SWEEPING_EDGE_ENCHANT = Collections.singletonList<CAEnchantment>(
CAEnchantment.getByKey(NamespacedKey.minecraft("sweeping_edge")) ?: CAEnchantment.getByKey(NamespacedKey.minecraft("sweeping_edge"))
CAEnchantment.getByKey(Enchantment.SWEEPING_EDGE.key)) ?: CAEnchantment.getByKey(Enchantment.SWEEPING_EDGE.key)
)
} }
@ -57,12 +58,12 @@ class EnchantConflictManager {
} }
fun addConflict(conflict: EnchantConflictGroup){ fun addConflict(conflict: EnchantConflictGroup) {
addConflictToEnchantments(conflict) addConflictToEnchantments(conflict)
conflictList.add(conflict) conflictList.add(conflict)
} }
fun removeConflict(conflict: EnchantConflictGroup){ fun removeConflict(conflict: EnchantConflictGroup) {
removeConflictFromEnchantments(conflict) removeConflictFromEnchantments(conflict)
conflictList.remove(conflict) conflictList.remove(conflict)
} }
@ -114,14 +115,14 @@ class EnchantConflictManager {
private fun getEnchantByIdentifier(enchantName: String): List<CAEnchantment> { private fun getEnchantByIdentifier(enchantName: String): List<CAEnchantment> {
val key = NamespacedKey.fromString(enchantName) val key = NamespacedKey.fromString(enchantName)
if(key != null){ if (key != null) {
val enchantment = CAEnchantment.getByKey(key) val enchantment = CAEnchantment.getByKey(key)
if(enchantment != null) return Collections.singletonList(enchantment) if (enchantment != null) return Collections.singletonList(enchantment)
} }
// Temporary solution for 1.20.5 // Temporary solution for 1.20.5
when(enchantName){ when (enchantName) {
"minecraft:sweeping", "sweeping", "minecraft:sweeping", "sweeping",
"minecraft:sweeping_edge", "sweeping_edge" -> { "minecraft:sweeping_edge", "sweeping_edge" -> {
return SWEEPING_EDGE_ENCHANT return SWEEPING_EDGE_ENCHANT
@ -169,22 +170,31 @@ class EnchantConflictManager {
return group return group
} }
fun isConflicting(appliedEnchants: Map<CAEnchantment, Int>, item: ItemStack, newEnchant: CAEnchantment): ConflictType { fun isConflicting(
appliedEnchants: Map<CAEnchantment, Int>,
item: ItemStack,
newEnchant: CAEnchantment
): ConflictType {
val mat = item.type val mat = item.type
CustomAnvil.verboseLog("Testing conflict for ${newEnchant.key} on ${mat.key}") CustomAnvil.verboseLog("Testing conflict for ${newEnchant.key} on ${mat.key}")
val conflictList = newEnchant.conflicts val conflictList = newEnchant.conflicts
var result = ConflictType.NO_CONFLICT var result = ConflictType.NO_CONFLICT
for (conflict in conflictList) { for (conflict in conflictList) {
CustomAnvil.verboseLog("Is against $conflict") val isBigConflict = conflict.getEnchants().size > 1
if (result == ConflictType.ITEM_CONFLICT && !isBigConflict) {
CustomAnvil.verboseLog("skipping small conflict ${conflict.name}")
continue
}
val allowed = conflict.allowed(appliedEnchants.keys, mat) val allowed = conflict.allowed(appliedEnchants.keys, mat)
CustomAnvil.verboseLog("Was against $conflict and conflicting: ${!allowed} ") CustomAnvil.verboseLog("Was against $conflict and conflicting: ${!allowed} ")
if (!allowed) { if (!allowed) {
if (conflict.getEnchants().size <= 1) { if (conflict.getEnchants().size <= 1) {
result = ConflictType.ITEM_CONFLICT result = ConflictType.ITEM_CONFLICT
CustomAnvil.verboseLog("Small conflict, continuing") CustomAnvil.verboseLog("Small conflict (${conflict.name}), continuing")
} else { } else {
CustomAnvil.verboseLog("Big conflict, probably stoping") CustomAnvil.verboseLog("Big conflict (${conflict.name}), stopping")
return ConflictType.ENCHANTMENT_CONFLICT return ConflictType.ENCHANTMENT_CONFLICT
} }
} }
@ -192,19 +202,20 @@ class EnchantConflictManager {
val immutableEnchants = Collections.unmodifiableMap(appliedEnchants) val immutableEnchants = Collections.unmodifiableMap(appliedEnchants)
for (appliedEnchant in appliedEnchants.keys) { for (appliedEnchant in appliedEnchants.keys) {
if(appliedEnchant is AdditionalTestEnchantment){ if (appliedEnchant is AdditionalTestEnchantment) {
val doConflict = appliedEnchant.isEnchantConflict(immutableEnchants, mat) val doConflict = appliedEnchant.isEnchantConflict(immutableEnchants, mat)
if(doConflict){ if (doConflict) {
CustomAnvil.verboseLog("Big conflict by additional test, stopping")
return ConflictType.ENCHANTMENT_CONFLICT return ConflictType.ENCHANTMENT_CONFLICT
} }
} }
} }
if((result != ConflictType.ITEM_CONFLICT) && (newEnchant is AdditionalTestEnchantment)){ if ((result != ConflictType.ITEM_CONFLICT) && (newEnchant is AdditionalTestEnchantment)) {
val partialItem = createPartialResult(item, immutableEnchants) val partialItem = createPartialResult(item, immutableEnchants)
if(newEnchant.isItemConflict(immutableEnchants, mat, partialItem)){ if (newEnchant.isItemConflict(immutableEnchants, mat, partialItem)) {
return ConflictType.ITEM_CONFLICT return ConflictType.ITEM_CONFLICT
} }
@ -217,8 +228,8 @@ class EnchantConflictManager {
val newItem = item.clone() val newItem = item.clone()
CAEnchantment.clearEnchants(newItem) CAEnchantment.clearEnchants(newItem)
enchantments.forEach{ enchantments.forEach { enchantment ->
enchantment -> enchantment.key.addEnchantmentUnsafe(newItem, enchantment.value) enchantment.key.addEnchantmentUnsafe(newItem, enchantment.value)
} }
return newItem return newItem
@ -247,7 +258,7 @@ enum class ConflictType(private val importance: Int) {
ENCHANTMENT_CONFLICT(2); ENCHANTMENT_CONFLICT(2);
fun getWorstConflict(otherConflict: ConflictType): ConflictType { fun getWorstConflict(otherConflict: ConflictType): ConflictType {
return if(this.importance > otherConflict.importance) this return if (this.importance > otherConflict.importance) this
else otherConflict else otherConflict
} }

View file

@ -19,6 +19,8 @@ import org.bukkit.event.Listener
import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.EnchantmentStorageMeta
import org.bukkit.inventory.meta.ItemMeta
import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.util.* import xyz.alexcrea.cuanvil.util.*
import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
@ -45,20 +47,30 @@ class PrepareAnvilListener : Listener {
// Should find player // Should find player
val player: HumanEntity = InventoryViewUtil.getInstance().getPlayer(event.view) val player: HumanEntity = InventoryViewUtil.getInstance().getPlayer(event.view)
// Test if the event should bypass custom anvil. // Test if custom anvil is bypassed before immutability test
if (DependencyManager.tryEventPreAnvilBypass(event, player)) return if (DependencyManager.earlyTryEventPreAnvilBypass(event, player)) return
val inventory = event.inventory val inventory = event.inventory
val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val second = inventory.getItem(ANVIL_INPUT_RIGHT) val second = inventory.getItem(ANVIL_INPUT_RIGHT)
if (isImmutable(first) || isImmutable(second)) {
CustomAnvil.verboseLog("Skipping anvil process as one of the two item is immutable")
event.result = null
return
}
// Test if the event should bypass custom anvil.
if (DependencyManager.tryEventPreAnvilBypass(event, player)) return
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
// Test custom recipe // Test custom recipe
if(testCustomRecipe(event, inventory, player, first, second)) return if (testCustomRecipe(event, inventory, player, first, second)) return
// Test rename lonely item // Test rename lonely item
if(second == null) { if (second == null) {
doRenaming(event, inventory, player, first) doRenaming(event, inventory, player, first)
return return
} }
@ -70,23 +82,51 @@ class PrepareAnvilListener : Listener {
} }
// Test for unit repair // Test for unit repair
if(testUnitRepair(event, inventory, player, first, second)) return if (testUnitRepair(event, inventory, player, first, second)) return
// Test for lore edit // Test for lore edit
if(testLoreEdit(event, inventory, player, first, second)) return if (testLoreEdit(event, inventory, player, first, second)) return
CustomAnvil.log("no anvil fuse type found") CustomAnvil.log("no anvil fuse type found")
event.result = null event.result = null
} }
private fun isImmutable(item: ItemStack?): Boolean {
if (item == null) return false
val meta = item.itemMeta
return meta != null &&
(hasImmutableEnchants(meta) || hasImmutableStoredEnchants(meta))
}
private fun hasImmutableEnchants(meta: ItemMeta): Boolean {
if (!meta.hasEnchants()) return false
for (enchant in meta.enchants.keys) {
if (ConfigOptions.isImmutable(enchant.key)) return true
}
return false
}
private fun hasImmutableStoredEnchants(meta: ItemMeta): Boolean {
if (meta !is EnchantmentStorageMeta || !meta.hasStoredEnchants()) return false
for (enchant in meta.storedEnchants.keys) {
if (ConfigOptions.isImmutable(enchant.key)) return true
}
return false
}
// return true if a custom recipe exist with these ingredients // return true if a custom recipe exist with these ingredients
private fun testCustomRecipe(event: PrepareAnvilEvent, inventory: AnvilInventory, private fun testCustomRecipe(
event: PrepareAnvilEvent, inventory: AnvilInventory,
player: HumanEntity, player: HumanEntity,
first: ItemStack, second: ItemStack?): Boolean { first: ItemStack, second: ItemStack?
): Boolean {
val recipe = CustomRecipeUtil.getCustomRecipe(first, second) val recipe = CustomRecipeUtil.getCustomRecipe(first, second)
CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}") CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}")
if(recipe == null) return false if (recipe == null) return false
val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second) val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second)
@ -94,7 +134,7 @@ class PrepareAnvilListener : Listener {
resultItem.amount *= amount resultItem.amount *= amount
event.result = resultItem event.result = resultItem
if(DependencyManager.tryTreatAnvilResult(event, resultItem)) return true if (DependencyManager.tryTreatAnvilResult(event, resultItem)) return true
// Maybe add an option on custom craft to ignore/not ignore penalty ?? // Maybe add an option on custom craft to ignore/not ignore penalty ??
var xpCost = recipe.xpCostPerCraft * amount var xpCost = recipe.xpCostPerCraft * amount
@ -105,8 +145,10 @@ class PrepareAnvilListener : Listener {
return true return true
} }
private fun doRenaming(event: PrepareAnvilEvent, inventory: AnvilInventory, private fun doRenaming(
player: HumanEntity, first: ItemStack) { event: PrepareAnvilEvent, inventory: AnvilInventory,
player: HumanEntity, first: ItemStack
) {
val resultItem = first.clone() val resultItem = first.clone()
var anvilCost = handleRename(resultItem, inventory, player) var anvilCost = handleRename(resultItem, inventory, player)
@ -118,7 +160,7 @@ class PrepareAnvilListener : Listener {
} }
event.result = resultItem event.result = resultItem
if(DependencyManager.tryTreatAnvilResult(event, resultItem)) return if (DependencyManager.tryTreatAnvilResult(event, resultItem)) return
anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY) anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY)
@ -131,18 +173,20 @@ class PrepareAnvilListener : Listener {
var sumCost = 0 var sumCost = 0
var useColor = false var useColor = false
if(ConfigOptions.renameColorPossible && inventoryName != null){ if (ConfigOptions.renameColorPossible && inventoryName != null) {
val resultString = StringBuilder(inventoryName) val resultString = StringBuilder(inventoryName)
useColor = AnvilColorUtil.handleColor(resultString, player, useColor = AnvilColorUtil.handleColor(
resultString, player,
ConfigOptions.permissionNeededForColor, ConfigOptions.permissionNeededForColor,
ConfigOptions.allowColorCode, ConfigOptions.allowHexadecimalColor, ConfigOptions.allowColorCode, ConfigOptions.allowHexadecimalColor,
AnvilColorUtil.ColorUseType.RENAME) AnvilColorUtil.ColorUseType.RENAME
)
if(useColor) { if (useColor) {
inventoryName = resultString.toString() inventoryName = resultString.toString()
sumCost+= ConfigOptions.useOfColorCost sumCost += ConfigOptions.useOfColorCost
} }
} }
@ -165,9 +209,11 @@ class PrepareAnvilListener : Listener {
return 0 return 0
} }
private fun doMerge(event: PrepareAnvilEvent, inventory: AnvilInventory, private fun doMerge(
event: PrepareAnvilEvent, inventory: AnvilInventory,
player: HumanEntity, player: HumanEntity,
first: ItemStack, second: ItemStack) { first: ItemStack, second: ItemStack
) {
val newEnchants = first.findEnchantments() val newEnchants = first.findEnchantments()
.combineWith(second.findEnchantments(), first, player) .combineWith(second.findEnchantments(), first, player)
val resultItem = first.clone() val resultItem = first.clone()
@ -195,14 +241,16 @@ class PrepareAnvilListener : Listener {
// Finally, we set result // Finally, we set result
event.result = resultItem event.result = resultItem
if(DependencyManager.tryTreatAnvilResult(event, resultItem)) return if (DependencyManager.tryTreatAnvilResult(event, resultItem)) return
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost) AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost)
} }
// return true if there is a valid unit repair with these ingredients // return true if there is a valid unit repair with these ingredients
private fun testUnitRepair(event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity, private fun testUnitRepair(
first: ItemStack, second: ItemStack): Boolean { event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity,
first: ItemStack, second: ItemStack
): Boolean {
val unitRepairAmount = first.getRepair(second) ?: return false val unitRepairAmount = first.getRepair(second) ?: return false
val resultItem = first.clone() val resultItem = first.clone()
@ -222,26 +270,27 @@ class PrepareAnvilListener : Listener {
return true return true
} }
event.result = resultItem event.result = resultItem
if(DependencyManager.tryTreatAnvilResult(event, resultItem)) return true if (DependencyManager.tryTreatAnvilResult(event, resultItem)) return true
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost) AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost)
return true return true
} }
private fun testLoreEdit(event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity, private fun testLoreEdit(
first: ItemStack, second: ItemStack): Boolean { event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity,
first: ItemStack, second: ItemStack
): Boolean {
val type = second.type val type = second.type
var result: ItemStack? = null var result: ItemStack? = null
val xpCost = AtomicInteger() val xpCost = AtomicInteger()
if(Material.WRITABLE_BOOK == type) { if (Material.WRITABLE_BOOK == type) {
result = AnvilLoreEditUtil.tryLoreEditByBook(player, first, second, xpCost) result = AnvilLoreEditUtil.tryLoreEditByBook(player, first, second, xpCost)
} } else if (Material.PAPER == type) {
else if(Material.PAPER == type) {
result = AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second, xpCost) result = AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second, xpCost)
} }
if(result == null || first == result) { if (result == null || first == result) {
CustomAnvil.log("lore edit, But input is same as output") CustomAnvil.log("lore edit, But input is same as output")
event.result = null event.result = null
return false return false

View file

@ -367,4 +367,4 @@ debug_log_verbose: false
# ProtocoLib may also be used if the server is in an "unsupported" version even if this option is disabled. # ProtocoLib may also be used if the server is in an "unsupported" version even if this option is disabled.
force_protocolib: false force_protocolib: false
configVersion: 1.8.0 configVersion: 1.11.0

View file

@ -0,0 +1,35 @@
"bracken:abstraction": ['#sword_enchant_conflict']
"bracken:antivenom": ['#protection_enchant_conflict']
"bracken:blinding_fix": ['minecraft:fire_aspect']
"bracken:boldness": ['#protection_enchant_conflict']
"bracken:butchering": ['#sword_enchant_conflict']
"bracken:defusing": ['#sword_enchant_conflict']
"bracken:dentine_touch": ['#tool_conflict']
"bracken:dullness_curse": ['minecraft:sharpness']
"bracken:famine_walker": ['#boot_conflict']
"bracken:ferocity": ['#sword_enchant_conflict']
"bracken:flame_walker": ['#boot_conflict']
"bracken:float_walker": ['#boot_conflict']
"bracken:flood_walker": ['#boot_conflict']
"bracken:fragility_curse": ['minecraft:unbreaking']
"bracken:freezing_fix": ['minecraft:fire_aspect']
"bracken:guarding": ['#sword_enchant_conflict']
"bracken:huskiness": ['#bracken_pet_armor']
"bracken:infused_fire_aspect_fix": ['minecraft:fire_aspect']
"bracken:infused_frost_walker_fix": ['#boot_conflict']
"bracken:infused_mending_fix": ['minecraft:mending']
"bracken:infused_thorns_fix": ['minecraft:thorns']
"bracken:lethargy_curse": ['bracken:ferocity']
"bracken:lifesteal": ['#bow_conflict']
"bracken:litheness": ['#bracken_pet_armor']
"bracken:lunar_concordance_armor": ['#bracken_lunarite_armor']
"bracken:lunar_concordance_weapon": ['#bracken_lunarite_weapons']
"bracken:maiming_curse": ['bracken:vitality_fix']
"bracken:mortality": ['#sword_enchant_conflict']
"bracken:poisoning_fix": ['minecraft:fire_aspect']
"bracken:pugnacity": ['#bracken_pet_armor']
"bracken:rending": ['#crossbow_conflict']
"bracken:silvered_fix": ['#all']
"bracken:vitality_fix": ['bracken:maiming_curse']
"bracken:weakening_fix": ['minecraft:fire_aspect']
"bracken:withering_fix": ['minecraft:fire_aspect']

View file

@ -0,0 +1,54 @@
"bracken:abstraction": ['melee_weapons', 'mace']
"bracken:antivenom": ['armors']
"bracken:astuteness_fix": ['armors']
"bracken:blinding_fix": ['can_unbreak']
"bracken:boldness": ['armors']
"bracken:butchering": ['melee_weapons', 'mace']
"bracken:chiseling_fix": ['axes', 'pickaxes', 'shovels', 'hoes']
"bracken:decaying_fix": ['can_unbreak']
"bracken:defusing": ['melee_weapons', 'mace']
"bracken:dentine_touch": ['axes', 'pickaxes', 'shovels', 'hoes']
"bracken:devouring_curse": ['melee_weapons', 'mace']
"bracken:dullness_curse": ['melee_weapons', 'mace']
"bracken:famine_walker": ['boots']
"bracken:ferocity": ['melee_weapons', 'mace']
"bracken:flame_walker": ['boots']
"bracken:float_walker": ['boots']
"bracken:flood_walker": ['boots']
"bracken:fragility_curse": ['can_unbreak']
"bracken:freezing_fix": ['can_unbreak']
"bracken:guarding": ['melee_weapons', 'mace']
"bracken:huskiness": ['pet_armor']
"bracken:infused_fire_aspect_fix": ['melee_weapons', 'mace']
"bracken:infused_frost_walker_fix": ['boots']
"bracken:infused_mending_fix": ['can_unbreak']
"bracken:infused_thorns_fix": ['armors']
"bracken:integrity_fix": ['armors']
"bracken:lethargy_curse": ['melee_weapons', 'mace']
"bracken:lifesteal": ['bow']
"bracken:litheness": ['pet_armor']
"bracken:maiming_curse": ['armors']
"bracken:mortality": ['melee_weapons', 'mace']
"bracken:poisoning_fix": ['can_unbreak']
"bracken:pugnacity": ['wolf_armor']
"bracken:pushback": ['armors']
"bracken:quenching_fix": ['armors']
"bracken:rending": ['crossbow']
"bracken:reverse_thorns": ['axes']
"bracken:searing_surface": ['armors']
"bracken:sentience_curse_1": ['can_unbreak']
"bracken:sentience_curse_2": ['can_unbreak']
"bracken:sentience_curse_3": ['can_unbreak']
"bracken:sentience_curse_4": ['can_unbreak']
"bracken:sentience_curse_5": ['can_unbreak']
"bracken:sentience_curse_6": ['can_unbreak']
"bracken:sentience_curse_7": ['can_unbreak']
"bracken:shrewdness": ['swords']
"bracken:silvered_fix": ['can_unbreak']
"bracken:spectrality_fix": ['melee_weapons', 'mace']
"bracken:splintering": ['bow']
"bracken:trampling": ['horse_armor']
"bracken:vitality_fix": ['armors']
"bracken:weakening_fix": ['can_unbreak']
"bracken:wisdom": ['tools']
"bracken:withering_fix": ['can_unbreak']

View file

@ -0,0 +1,15 @@
horse_armor:
items:
- leather_horse_armor
- iron_horse_armor
- golden_horse_armor
- diamond_horse_armor
wolf_armor:
items:
- wolf_armor
pet_armor:
groups:
- wolf_armor
- horse_armor

View file

@ -126,7 +126,7 @@ wearable:
groups: groups:
- armors - armors
tools: pickaxes:
type: include type: include
items: items:
- wooden_pickaxe - wooden_pickaxe
@ -135,19 +135,33 @@ tools:
- diamond_pickaxe - diamond_pickaxe
- golden_pickaxe - golden_pickaxe
- netherite_pickaxe - netherite_pickaxe
shovels:
type: include
items:
- wooden_shovel - wooden_shovel
- stone_shovel - stone_shovel
- iron_shovel - iron_shovel
- diamond_shovel - diamond_shovel
- golden_shovel - golden_shovel
- netherite_shovel - netherite_shovel
hoes:
type: include
items:
- wooden_hoe - wooden_hoe
- stone_hoe - stone_hoe
- iron_hoe - iron_hoe
- diamond_hoe - diamond_hoe
- golden_hoe - golden_hoe
- netherite_hoe - netherite_hoe
tools:
type: include
groups: groups:
- pickaxes
- shovels
- hoes
- axes - axes
enchanted_book: enchanted_book: