From 078fb1bbbd6294673195aba98b93fc9b92d829d9 Mon Sep 17 00:00:00 2001
From: Aya <31237389+tal5@users.noreply.github.com>
Date: Sun, 29 Jun 2025 23:29:48 +0100
Subject: [PATCH 1/3] Enchantment Scripts Back-support
---
dist/pom.xml | 4 -
.../nms/interfaces/EnchantmentHelper.java | 142 +++++
.../scripts/containers/ContainerRegistry.java | 6 +-
.../core/EnchantmentScriptContainer.java | 40 +-
.../utilities/BukkitImplDeprecations.java | 3 +
.../denizen/utilities/depends/Depends.java | 11 +
.../v1_17/helpers/EnchantmentHelperImpl.java | 2 +-
.../v1_18/helpers/EnchantmentHelperImpl.java | 2 +-
.../v1_19/helpers/EnchantmentHelperImpl.java | 2 +-
.../v1_20/helpers/EnchantmentHelperImpl.java | 182 ++++--
.../v1_21/helpers/EnchantmentHelperImpl.java | 554 +++++++++++++-----
11 files changed, 729 insertions(+), 219 deletions(-)
diff --git a/dist/pom.xml b/dist/pom.xml
index 0ce5cb5745..555ea3730e 100644
--- a/dist/pom.xml
+++ b/dist/pom.xml
@@ -155,10 +155,6 @@
org.objectweb
com.denizenscript.shaded.org.objectweb
-
- org.apache
- com.denizenscript.shaded.org.apache
-
diff --git a/plugin/src/main/java/com/denizenscript/denizen/nms/interfaces/EnchantmentHelper.java b/plugin/src/main/java/com/denizenscript/denizen/nms/interfaces/EnchantmentHelper.java
index 1991370151..806b48fba3 100644
--- a/plugin/src/main/java/com/denizenscript/denizen/nms/interfaces/EnchantmentHelper.java
+++ b/plugin/src/main/java/com/denizenscript/denizen/nms/interfaces/EnchantmentHelper.java
@@ -1,9 +1,27 @@
package com.denizenscript.denizen.nms.interfaces;
+import com.denizenscript.denizen.Denizen;
import com.denizenscript.denizen.scripts.containers.core.EnchantmentScriptContainer;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Entity;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
+import org.bukkit.event.inventory.InventoryEvent;
+import org.bukkit.event.inventory.PrepareAnvilEvent;
+import org.bukkit.event.inventory.PrepareGrindstoneEvent;
+import org.bukkit.inventory.EntityEquipment;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.Map;
public class EnchantmentHelper {
@@ -46,4 +64,128 @@ public float getDamageBonus(Enchantment enchantment, int level, String type) {
public int getDamageProtection(Enchantment enchantment, int level, EntityDamageEvent.DamageCause type, Entity attacker) {
throw new UnsupportedOperationException();
}
+
+ public boolean eventsRegistered = false;
+
+ public void verifyEventsRegistered() {
+ if (eventsRegistered) {
+ return;
+ }
+ Bukkit.getPluginManager().registerEvents(new EnchantmentBackSupportEvents(), Denizen.getInstance());
+ eventsRegistered = true;
+ }
+
+ static class EnchantmentBackSupportEvents implements Listener {
+
+ @EventHandler
+ public void on(PrepareGrindstoneEvent event) {
+ for (ItemStack input : event.getInventory().getContents()) {
+ if (trySendingInventoryUpdate(input, event)) {
+ break;
+ }
+ }
+ }
+
+ @EventHandler
+ public void on(PrepareAnvilEvent event) {
+ trySendingInventoryUpdate(event.getResult(), event);
+ }
+
+ public static boolean trySendingInventoryUpdate(ItemStack item, InventoryEvent event) {
+ if (item == null || item.getType() == Material.AIR) {
+ return false;
+ }
+ ItemMeta itemMeta = item.getItemMeta();
+ if (itemMeta == null || !itemMeta.hasEnchants()) {
+ return false;
+ }
+ for (Enchantment enchantment : itemMeta.getEnchants().keySet()) {
+ if (EnchantmentScriptContainer.getScriptFromEnchantment(enchantment) != null) {
+ Bukkit.getScheduler().runTaskLater(Denizen.getInstance(), () -> {
+ for (HumanEntity viewer : event.getViewers()) {
+ if (viewer instanceof Player player) {
+ player.updateInventory();
+ }
+ }
+ }, 1);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void on(EntityDamageByEntityEvent event) {
+ if (event.getDamager() instanceof LivingEntity attacker && attacker.getEquipment() != null) {
+ EntityEquipment equipment = attacker.getEquipment();
+ processEnchantmentScripts(equipment.getItemInMainHand(), event, EnchantmentScriptContainer::doPostAttack);
+ processEnchantmentScripts(equipment.getItemInOffHand(), event, EnchantmentScriptContainer::doPostAttack);
+ }
+ if (event.getEntity() instanceof LivingEntity attacked && attacked.getEquipment() != null) {
+ EntityEquipment equipment = attacked.getEquipment();
+ processEnchantmentScripts(equipment.getItemInMainHand(), event, EnchantmentScriptContainer::doPostHurt);
+ processEnchantmentScripts(equipment.getItemInOffHand(), event, EnchantmentScriptContainer::doPostHurt);
+ processEnchantmentScripts(equipment.getHelmet(), event, EnchantmentScriptContainer::doPostHurt);
+ processEnchantmentScripts(equipment.getChestplate(), event, EnchantmentScriptContainer::doPostHurt);
+ processEnchantmentScripts(equipment.getLeggings(), event, EnchantmentScriptContainer::doPostHurt);
+ processEnchantmentScripts(equipment.getBoots(), event, EnchantmentScriptContainer::doPostHurt);
+ }
+ }
+
+ @FunctionalInterface
+ public interface EnchantmentSubScriptRunner {
+ void runSubScript(EnchantmentScriptContainer enchantmentScript, Entity attacker, Entity victim, int level);
+ }
+
+ public static void processEnchantmentScripts(ItemStack item, EntityDamageByEntityEvent event, EnchantmentSubScriptRunner subScriptRunner) {
+ if (item == null || item.getType() == Material.AIR) {
+ return;
+ }
+ ItemMeta itemMeta = item.getItemMeta();
+ if (itemMeta == null || !itemMeta.hasEnchants()) {
+ return;
+ }
+ for (Map.Entry entry : itemMeta.getEnchants().entrySet()) {
+ EnchantmentScriptContainer enchantmentScript = EnchantmentScriptContainer.getScriptFromEnchantment(entry.getKey());
+ if (enchantmentScript != null) {
+ subScriptRunner.runSubScript(enchantmentScript, event.getDamager(), event.getEntity(), entry.getValue());
+ }
+ }
+ }
+ }
+
+ public enum Rarity {
+ COMMON(10, 1),
+ UNCOMMON(5, 2),
+ RARE(2, 4),
+ VERY_RARE(1, 8);
+
+ final int weight, anvilCost;
+
+ Rarity(int weight, int anvilCost) {
+ this.weight = weight;
+ this.anvilCost = anvilCost;
+ }
+
+ public int getWeight() {
+ return weight;
+ }
+
+ public int getAnvilCost() {
+ return anvilCost;
+ }
+
+ public static Rarity fromWeight(int weight) {
+ int smallestDistance = Integer.MAX_VALUE;
+ Rarity closest = null;
+ for (Rarity rarity : Rarity.values()) {
+ int distance = Math.abs(weight - rarity.getWeight());
+ if (distance < smallestDistance) {
+ smallestDistance = distance;
+ closest = rarity;
+ }
+ }
+ return closest;
+ }
+ }
}
diff --git a/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/ContainerRegistry.java b/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/ContainerRegistry.java
index 26ccb3050f..5e152d1548 100644
--- a/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/ContainerRegistry.java
+++ b/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/ContainerRegistry.java
@@ -1,5 +1,7 @@
package com.denizenscript.denizen.scripts.containers;
+import com.denizenscript.denizen.nms.NMSHandler;
+import com.denizenscript.denizen.nms.NMSVersion;
import com.denizenscript.denizen.scripts.containers.core.*;
import com.denizenscript.denizen.utilities.depends.Depends;
import com.denizenscript.denizencore.scripts.ScriptRegistry;
@@ -13,7 +15,9 @@ public static void registerMainContainers() {
if (Depends.vault != null) {
ScriptRegistry._registerType("economy", EconomyScriptContainer.class);
}
- ScriptRegistry._registerType("enchantment", EnchantmentScriptContainer.class);
+ if (NMSHandler.getVersion().isAtMost(NMSVersion.v1_20) || Depends.denizenEnchantmentFix != null) {
+ ScriptRegistry._registerType("enchantment", EnchantmentScriptContainer.class);
+ }
ScriptRegistry._registerType("entity", EntityScriptContainer.class);
ScriptRegistry._registerType("format", FormatScriptContainer.class);
ScriptRegistry._registerType("interact", InteractScriptContainer.class);
diff --git a/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/core/EnchantmentScriptContainer.java b/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/core/EnchantmentScriptContainer.java
index 29c080c550..4db510d2ca 100644
--- a/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/core/EnchantmentScriptContainer.java
+++ b/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/core/EnchantmentScriptContainer.java
@@ -1,32 +1,40 @@
package com.denizenscript.denizen.scripts.containers.core;
+import com.denizenscript.denizen.Denizen;
import com.denizenscript.denizen.nms.NMSHandler;
+import com.denizenscript.denizen.nms.NMSVersion;
import com.denizenscript.denizen.objects.EntityTag;
import com.denizenscript.denizen.objects.ItemTag;
import com.denizenscript.denizen.tags.BukkitTagContext;
+import com.denizenscript.denizen.utilities.BukkitImplDeprecations;
import com.denizenscript.denizen.utilities.FormattedTextHelper;
-import com.denizenscript.denizencore.utilities.debugging.Debug;
import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
+import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.objects.core.ScriptTag;
import com.denizenscript.denizencore.scripts.ScriptEntry;
import com.denizenscript.denizencore.scripts.containers.ScriptContainer;
import com.denizenscript.denizencore.scripts.queues.ContextSource;
import com.denizenscript.denizencore.scripts.queues.core.InstantQueue;
+import com.denizenscript.denizencore.tags.ParseableTag;
import com.denizenscript.denizencore.tags.TagContext;
import com.denizenscript.denizencore.tags.TagManager;
import com.denizenscript.denizencore.utilities.AsciiMatcher;
import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.denizenscript.denizencore.utilities.YamlConfiguration;
+import com.denizenscript.denizencore.utilities.debugging.Debug;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import org.bukkit.Bukkit;
+import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
public class EnchantmentScriptContainer extends ScriptContainer {
@@ -172,12 +180,21 @@ public class EnchantmentScriptContainer extends ScriptContainer {
// -->
public static AsciiMatcher descriptionCharsAllowed = new AsciiMatcher(AsciiMatcher.LETTERS_LOWER + "_");
- public static HashMap registeredEnchantmentContainers = new HashMap<>();
+ public static Map registeredEnchantmentContainers = new ConcurrentHashMap<>();
public static class EnchantmentReference {
public EnchantmentScriptContainer script;
}
+ public static EnchantmentScriptContainer getScriptFromEnchantment(Enchantment enchantment) {
+ NamespacedKey key = enchantment.getKey();
+ if (!key.getNamespace().equals("denizen")) {
+ return null;
+ }
+ EnchantmentReference reference = registeredEnchantmentContainers.get(key.getKey());
+ return reference != null ? reference.script : null;
+ }
+
public EnchantmentScriptContainer(YamlConfiguration configurationSection, String scriptContainerName) {
super(configurationSection, scriptContainerName);
canRunScripts = false;
@@ -215,6 +232,9 @@ public EnchantmentScriptContainer(YamlConfiguration configurationSection, String
enchantment = old.enchantment;
}
}
+ if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20)) {
+ BukkitImplDeprecations.enchantmentScriptContainers.warn(this);
+ }
}
public int minLevel, maxLevel;
@@ -251,6 +271,16 @@ public String autoTag(String value, ContextSource src) {
return TagManager.tag(value, context);
}
+ public ObjectTag autoTag(ParseableTag tag, ContextSource src) {
+ if (tag == null) {
+ return null;
+ }
+ validateThread();
+ TagContext context = new BukkitTagContext(null, new ScriptTag(this));
+ context.contextSource = src;
+ return tag.parse(context);
+ }
+
public String autoTagForLevel(String value, int level) {
ContextSource.SimpleMap src = new ContextSource.SimpleMap();
src.contexts = new HashMap<>();
@@ -335,7 +365,7 @@ public void doPostAttack(Entity attacker, Entity victim, int level) {
runSubScript("after attack", attacker, victim, attacker, level);
}
- public void doPostHurt(Entity victim, Entity attacker, int level) {
+ public void doPostHurt(Entity attacker, Entity victim, int level) {
runSubScript("after hurt", attacker, victim, victim, level);
}
@@ -360,4 +390,8 @@ public int getMaxCost(int level) {
maxCosts.put(level, cost);
return cost;
}
+
+ public NamespacedKey getKey() {
+ return new NamespacedKey(Denizen.getInstance(), id);
+ }
}
diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/BukkitImplDeprecations.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/BukkitImplDeprecations.java
index 72e59895c7..570b2bc572 100644
--- a/plugin/src/main/java/com/denizenscript/denizen/utilities/BukkitImplDeprecations.java
+++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/BukkitImplDeprecations.java
@@ -355,6 +355,9 @@ public class BukkitImplDeprecations {
// Bump once 1.21 is the minimum supported version (as that is where boat types were split)
public static Warning gettingBoatType = new SlowWarning("gettingBoatType", "Getting boat wood types is deprecated, as separate boat types are separate entity types now: should check the entity type.");
+ // Added 2025/06/28
+ public static Warning enchantmentScriptContainers = new SlowWarning("enchantmentScriptContainers", "Enchantment script containers are deprecated due to extensive internal changes. We recommend utilizing datapacks (which also make the enchantments show up on the client like vanilla ones), see https://misode.github.io/enchantment.");
+
// ==================== VERY SLOW deprecations ====================
// These are only shown minimally, so server owners are aware of them but not bugged by them. Only servers with active scripters (using 'ex reload') will see them often.
diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/depends/Depends.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/depends/Depends.java
index 63b507da2d..914dd45365 100644
--- a/plugin/src/main/java/com/denizenscript/denizen/utilities/depends/Depends.java
+++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/depends/Depends.java
@@ -20,11 +20,13 @@ public class Depends {
public static Permission permissions = null;
public static Chat chat = null;
public static Plugin vault = null;
+ public static Plugin denizenEnchantmentFix = null;
public static void initialize() {
setupBungee();
setupVault();
setupCitizens();
+ setupEnchantmentFix();
}
public static void setupVault() {
@@ -99,4 +101,13 @@ public static boolean setupCitizens() {
}
return citizens != null;
}
+
+ public static boolean setupEnchantmentFix() {
+ Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin("DenizenEnchantmentFix");
+ if (plugin == null || !plugin.isEnabled()) {
+ return false;
+ }
+ denizenEnchantmentFix = plugin;
+ return true;
+ }
}
diff --git a/v1_17/src/main/java/com/denizenscript/denizen/nms/v1_17/helpers/EnchantmentHelperImpl.java b/v1_17/src/main/java/com/denizenscript/denizen/nms/v1_17/helpers/EnchantmentHelperImpl.java
index be378e2c3d..979194f5cc 100644
--- a/v1_17/src/main/java/com/denizenscript/denizen/nms/v1_17/helpers/EnchantmentHelperImpl.java
+++ b/v1_17/src/main/java/com/denizenscript/denizen/nms/v1_17/helpers/EnchantmentHelperImpl.java
@@ -104,7 +104,7 @@ public void doPostAttack(LivingEntity attacker, Entity victim, int level) {
}
@Override
public void doPostHurt(LivingEntity victim, Entity attacker, int level) {
- script.script.doPostHurt(victim.getBukkitEntity(), attacker.getBukkitEntity(), level);
+ script.script.doPostHurt(attacker.getBukkitEntity(), victim.getBukkitEntity(), level);
}
@Override
public boolean isTreasureOnly() {
diff --git a/v1_18/src/main/java/com/denizenscript/denizen/nms/v1_18/helpers/EnchantmentHelperImpl.java b/v1_18/src/main/java/com/denizenscript/denizen/nms/v1_18/helpers/EnchantmentHelperImpl.java
index 6fa2f7d5f2..ffe12eaaa2 100644
--- a/v1_18/src/main/java/com/denizenscript/denizen/nms/v1_18/helpers/EnchantmentHelperImpl.java
+++ b/v1_18/src/main/java/com/denizenscript/denizen/nms/v1_18/helpers/EnchantmentHelperImpl.java
@@ -108,7 +108,7 @@ public void doPostAttack(LivingEntity attacker, Entity victim, int level) {
}
@Override
public void doPostHurt(LivingEntity victim, Entity attacker, int level) {
- script.script.doPostHurt(victim.getBukkitEntity(), attacker.getBukkitEntity(), level);
+ script.script.doPostHurt(attacker.getBukkitEntity(), victim.getBukkitEntity(), level);
}
@Override
public boolean isTreasureOnly() {
diff --git a/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/helpers/EnchantmentHelperImpl.java b/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/helpers/EnchantmentHelperImpl.java
index 395b31fb0a..d0e9debf78 100644
--- a/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/helpers/EnchantmentHelperImpl.java
+++ b/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/helpers/EnchantmentHelperImpl.java
@@ -110,7 +110,7 @@ public void doPostAttack(LivingEntity attacker, Entity victim, int level) {
}
@Override
public void doPostHurt(LivingEntity victim, Entity attacker, int level) {
- script.script.doPostHurt(victim.getBukkitEntity(), attacker.getBukkitEntity(), level);
+ script.script.doPostHurt(attacker.getBukkitEntity(), victim.getBukkitEntity(), level);
}
@Override
public boolean isTreasureOnly() {
diff --git a/v1_20/src/main/java/com/denizenscript/denizen/nms/v1_20/helpers/EnchantmentHelperImpl.java b/v1_20/src/main/java/com/denizenscript/denizen/nms/v1_20/helpers/EnchantmentHelperImpl.java
index c8b1400c6a..cb7d2d8c2f 100644
--- a/v1_20/src/main/java/com/denizenscript/denizen/nms/v1_20/helpers/EnchantmentHelperImpl.java
+++ b/v1_20/src/main/java/com/denizenscript/denizen/nms/v1_20/helpers/EnchantmentHelperImpl.java
@@ -9,34 +9,127 @@
import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.denizenscript.denizencore.utilities.ReflectionHelper;
import com.denizenscript.denizencore.utilities.debugging.Debug;
+import it.unimi.dsi.fastutil.ints.Int2IntFunction;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
+import net.minecraft.tags.EntityTypeTags;
+import net.minecraft.tags.ItemTags;
+import net.minecraft.tags.TagKey;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.item.Item;
import org.bukkit.NamespacedKey;
import org.bukkit.craftbukkit.v1_20_R4.enchantments.CraftEnchantment;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack;
-import org.bukkit.craftbukkit.v1_20_R4.util.CraftNamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.event.entity.EntityDamageEvent;
import java.lang.reflect.Field;
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Optional;
public class EnchantmentHelperImpl extends EnchantmentHelper {
public static final Field REGISTRY_FROZEN = ReflectionHelper.getFields(MappedRegistry.class).get(ReflectionMappingsInfo.MappedRegistry_frozen, boolean.class);
public static final Field REGISTRY_INTRUSIVE_HOLDERS = ReflectionHelper.getFields(MappedRegistry.class).get(ReflectionMappingsInfo.MappedRegistry_unregisteredIntrusiveHolders, Map.class);
+ public static final TagKey- NON_EXISTENT_ITEM_TAG = new TagKey<>(Registries.ITEM, new ResourceLocation("denizen", "aeg9jw0059ghg00ig3wiij"));
+
+ public enum MobType {
+ ILLAGER(EntityTypeTags.ILLAGER),
+ UNDEAD(EntityTypeTags.UNDEAD),
+ WATER(EntityTypeTags.AQUATIC),
+ ARTHROPOD(EntityTypeTags.ARTHROPOD),
+ UNDEFINED(null);
+
+ final TagKey> nmsTagKey;
+
+ MobType(TagKey> nmsTagKey) {
+ this.nmsTagKey = nmsTagKey;
+ }
+
+ public TagKey> getNmsTagKey() {
+ return nmsTagKey;
+ }
+
+ public static final MobType[] WITH_TAGS = {ILLAGER, UNDEAD, WATER, ARTHROPOD};
+
+ public static MobType fromEntityType(EntityType> nmsEntityType) {
+ for (MobType mobType : WITH_TAGS) {
+ if (nmsEntityType.is(mobType.getNmsTagKey())) {
+ return mobType;
+ }
+ }
+ return UNDEFINED;
+ }
+ }
+
+ public enum EnchantmentCategory {
+ ARMOR(ItemTags.ARMOR_ENCHANTABLE),
+ ARMOR_FEET(ItemTags.FOOT_ARMOR_ENCHANTABLE),
+ ARMOR_LEGS(ItemTags.LEG_ARMOR_ENCHANTABLE),
+ ARMOR_CHEST(ItemTags.CHEST_ARMOR_ENCHANTABLE),
+ ARMOR_HEAD(ItemTags.HEAD_ARMOR_ENCHANTABLE),
+ WEAPON(ItemTags.WEAPON_ENCHANTABLE),
+ DIGGER(ItemTags.MINING_ENCHANTABLE),
+ FISHING_ROD(ItemTags.FISHING_ENCHANTABLE),
+ TRIDENT(ItemTags.TRIDENT_ENCHANTABLE),
+ BREAKABLE(ItemTags.DURABILITY_ENCHANTABLE),
+ BOW(ItemTags.BOW_ENCHANTABLE),
+ WEARABLE(ItemTags.EQUIPPABLE_ENCHANTABLE),
+ CROSSBOW(ItemTags.CROSSBOW_ENCHANTABLE),
+ VANISHABLE(ItemTags.VANISHING_ENCHANTABLE);
+
+ final TagKey
- nmsTagKey;
+
+ EnchantmentCategory(TagKey
- nmsTagKey) {
+ this.nmsTagKey = nmsTagKey;
+ }
+
+ public TagKey
- getNmsTagKey() {
+ return nmsTagKey;
+ }
+ }
+
+ public static net.minecraft.world.item.enchantment.Enchantment.Cost tryParseCost(EnchantmentScriptContainer scriptContainer, String costTag, Int2IntFunction costMethod) {
+ if (costTag.equals("")) {
+ return net.minecraft.world.item.enchantment.Enchantment.dynamicCost(1, 1);
+ }
+ if (costTag.startsWith(" MobType.ILLAGER;
-// case "undead" -> MobType.UNDEAD;
-// case "water" -> MobType.WATER;
-// case "arthropod" -> MobType.ARTHROPOD;
-// default -> MobType.UNDEFINED;
-// };
-// return ((CraftEnchantment) enchantment).getHandle().getDamageBonus(level, mobType);
-// }
+ @Override
+ public float getDamageBonus(Enchantment enchantment, int level, String type) {
+ EntityType> nmsEntityType = Optional.ofNullable(MobType.valueOf(CoreUtilities.toUpperCase(type)).getNmsTagKey())
+ .flatMap(BuiltInRegistries.ENTITY_TYPE::getTag)
+ .map(holders -> holders.get(0).value())
+ .orElse((EntityType) EntityType.PIG);
+ return ((CraftEnchantment) enchantment).getHandle().getDamageBonus(level, nmsEntityType);
+ }
@Override
public int getDamageProtection(Enchantment enchantment, int level, EntityDamageEvent.DamageCause type, org.bukkit.entity.Entity attacker) {
diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/helpers/EnchantmentHelperImpl.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/helpers/EnchantmentHelperImpl.java
index ba72802159..e319511abc 100644
--- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/helpers/EnchantmentHelperImpl.java
+++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/helpers/EnchantmentHelperImpl.java
@@ -1,195 +1,457 @@
package com.denizenscript.denizen.nms.v1_21.helpers;
import com.denizenscript.denizen.nms.interfaces.EnchantmentHelper;
+import com.denizenscript.denizen.nms.v1_21.Handler;
+import com.denizenscript.denizen.nms.v1_21.ReflectionMappingsInfo;
+import com.denizenscript.denizen.scripts.containers.core.EnchantmentScriptContainer;
+import com.denizenscript.denizen.utilities.FormattedTextHelper;
+import com.denizenscript.denizencore.DenizenCore;
+import com.denizenscript.denizencore.tags.TagManager;
+import com.denizenscript.denizencore.utilities.CoreUtilities;
+import com.denizenscript.denizencore.utilities.ReflectionHelper;
+import com.denizenscript.denizencore.utilities.debugging.Debug;
+import com.mojang.datafixers.util.Either;
+import com.mojang.serialization.Lifecycle;
+import com.mojang.serialization.MapCodec;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+import it.unimi.dsi.fastutil.ints.Int2IntFunction;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import net.md_5.bungee.api.ChatColor;
+import net.minecraft.core.*;
+import net.minecraft.core.component.DataComponentMap;
+import net.minecraft.core.component.DataComponentType;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.protocol.configuration.ClientboundRegistryDataPacket;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.packs.repository.BuiltInPackSource;
+import net.minecraft.tags.EnchantmentTags;
+import net.minecraft.tags.EntityTypeTags;
+import net.minecraft.tags.ItemTags;
+import net.minecraft.tags.TagKey;
+import net.minecraft.world.damagesource.DamageSource;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EntitySpawnReason;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.entity.EquipmentSlotGroup;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.Items;
+import net.minecraft.world.item.enchantment.ConditionalEffect;
+import net.minecraft.world.item.enchantment.Enchantment;
+import net.minecraft.world.item.enchantment.EnchantmentEffectComponents;
+import net.minecraft.world.item.enchantment.LevelBasedValue;
+import net.minecraft.world.item.enchantment.effects.AddValue;
+import net.minecraft.world.item.enchantment.effects.EnchantmentValueEffect;
+import net.minecraft.world.level.storage.loot.LootContext;
+import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
+import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
+import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
+import org.apache.commons.lang3.mutable.MutableFloat;
+import org.bukkit.craftbukkit.v1_21_R5.CraftRegistry;
+import org.bukkit.craftbukkit.v1_21_R5.enchantments.CraftEnchantment;
+import org.bukkit.craftbukkit.v1_21_R5.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.v1_21_R5.util.CraftNamespacedKey;
+import org.bukkit.event.entity.EntityDamageEvent;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.function.BiFunction;
+import java.util.function.Predicate;
public class EnchantmentHelperImpl extends EnchantmentHelper {
- // TODO: 1.21: Enchantments were entirely reworked, need to update this
- /*
+
+ public static class DynamicHolderSet extends HolderSet.ListBacked {
+
+ final Registry backing;
+ final Predicate filter;
+
+ public DynamicHolderSet(Registry backing, Predicate filter) {
+ this.backing = backing;
+ this.filter = filter;
+ }
+
+ @Override
+ protected List> contents() {
+ List> contents = new ArrayList<>();
+ backing.listElements().forEach(holder -> {
+ if (filter.test(holder.value())) {
+ contents.add(holder);
+ }
+ });
+ return contents;
+ }
+
+ @Override
+ public boolean isBound() {
+ return true;
+ }
+
+ @Override
+ public Either, List>> unwrap() {
+ return Either.right(contents());
+ }
+
+ @Override
+ public boolean contains(Holder holder) {
+ T value = holder.value();
+ return backing.getId(value) != -1 && filter.test(value);
+ }
+
+ @Override
+ public Optional> unwrapKey() {
+ return Optional.empty();
+ }
+
+ @Override
+ public String toString() {
+ return "DynamicHolderSet[backing=" + backing + ']';
+ }
+ }
+
+ public static class DynamicContextAwareLevelValue implements LevelBasedValue, LootItemCondition {
+
+ final BiFunction calculator;
+ LootContext currentContext;
+
+ public DynamicContextAwareLevelValue(BiFunction calculator) {
+ this.calculator = calculator;
+ }
+
+ @Override
+ public float calculate(int level) {
+ if (currentContext == null) {
+ throw new IllegalStateException("Missing current LootContext.");
+ }
+ float calculated = calculator.apply(level, currentContext);
+ currentContext = null;
+ return calculated;
+ }
+
+ @Override
+ public MapCodec extends LevelBasedValue> codec() {
+ throw new UnsupportedOperationException("Tried getting codec.");
+ }
+
+ @Override
+ public LootItemConditionType getType() {
+ throw new UnsupportedOperationException("Tried getting loot item condition type.");
+ }
+
+ @Override
+ public boolean test(LootContext lootContext) {
+ currentContext = lootContext;
+ return true;
+ }
+ }
+
+ public enum EnchantmentCategory {
+ ARMOR(ItemTags.ARMOR_ENCHANTABLE),
+ ARMOR_FEET(ItemTags.FOOT_ARMOR_ENCHANTABLE),
+ ARMOR_LEGS(ItemTags.LEG_ARMOR_ENCHANTABLE),
+ ARMOR_CHEST(ItemTags.CHEST_ARMOR_ENCHANTABLE),
+ ARMOR_HEAD(ItemTags.HEAD_ARMOR_ENCHANTABLE),
+ WEAPON(ItemTags.WEAPON_ENCHANTABLE),
+ DIGGER(ItemTags.MINING_ENCHANTABLE),
+ FISHING_ROD(ItemTags.FISHING_ENCHANTABLE),
+ TRIDENT(ItemTags.TRIDENT_ENCHANTABLE),
+ BREAKABLE(ItemTags.DURABILITY_ENCHANTABLE),
+ BOW(ItemTags.BOW_ENCHANTABLE),
+ WEARABLE(ItemTags.EQUIPPABLE_ENCHANTABLE),
+ CROSSBOW(ItemTags.CROSSBOW_ENCHANTABLE),
+ VANISHABLE(ItemTags.VANISHING_ENCHANTABLE);
+
+ final TagKey
- nmsTagKey;
+
+ EnchantmentCategory(TagKey
- nmsTagKey) {
+ this.nmsTagKey = nmsTagKey;
+ }
+
+ public HolderSet
- getItems() {
+ return BuiltInRegistries.ITEM.get(nmsTagKey).orElseThrow();
+ }
+ }
+
+ public enum MobType {
+ ILLAGER(EntityTypeTags.ILLAGER),
+ UNDEAD(EntityTypeTags.UNDEAD),
+ WATER(EntityTypeTags.AQUATIC),
+ ARTHROPOD(EntityTypeTags.ARTHROPOD),
+ UNDEFINED(null);
+
+ final TagKey> nmsTagKey;
+
+ MobType(TagKey> nmsTagKey) {
+ this.nmsTagKey = nmsTagKey;
+ }
+
+ public TagKey> getNmsTagKey() {
+ return nmsTagKey;
+ }
+
+ public static final MobType[] WITH_TAGS = {ILLAGER, UNDEAD, WATER, ARTHROPOD};
+
+ public static MobType fromEntityType(EntityType> nmsEntityType) {
+ for (MobType mobType : WITH_TAGS) {
+ if (nmsEntityType.is(mobType.getNmsTagKey())) {
+ return mobType;
+ }
+ }
+ return UNDEFINED;
+ }
+ }
+
+ public static final RegistrationInfo NO_NETWORKING_REGISTRATION_INFO = new RegistrationInfo(Optional.of(BuiltInPackSource.CORE_PACK_INFO), Lifecycle.stable());
public static final Field REGISTRY_FROZEN = ReflectionHelper.getFields(MappedRegistry.class).get(ReflectionMappingsInfo.MappedRegistry_frozen, boolean.class);
public static final Field REGISTRY_INTRUSIVE_HOLDERS = ReflectionHelper.getFields(MappedRegistry.class).get(ReflectionMappingsInfo.MappedRegistry_unregisteredIntrusiveHolders, Map.class);
+ public static final Field REGISTRY_ALL_TAGS = ReflectionHelper.getFields(MappedRegistry.class).get("allTags");
+ public static final MethodHandle MAPPED_REGISTRY_TAG_SET_UNBOUND = ReflectionHelper.getMethodHandle(REGISTRY_ALL_TAGS.getType(), "unbound");
+
+ public static Enchantment.Cost tryParseCost(EnchantmentScriptContainer scriptContainer, String costTag, Int2IntFunction costMethod) {
+ if (costTag.equals("")) {
+ return Enchantment.dynamicCost(1, 1);
+ }
+ if (costTag.startsWith(" nmsEnchantmentRegistry = (MappedRegistry) CraftRegistry.getMinecraftRegistry(Registries.ENCHANTMENT);
+ // Slot groups
+ List nmsSlotGroups = new ArrayList<>(script.script.slots.size());
+ for (String slot : script.script.slots) {
+ nmsSlotGroups.add(EquipmentSlotGroup.valueOf(CoreUtilities.toUpperCase(slot)));
}
- // TODO: 1.20.6: rarity is provided as an int, can make our own mirror enum; categories seemed to only over control #canEnchant(ItemStack), so can probably safely phase them out?
- net.minecraft.world.item.enchantment.Enchantment.Rarity.valueOf(script.script.rarity), EnchantmentCategory.valueOf(script.script.category), slots
- net.minecraft.world.item.enchantment.Enchantment nmsEnchant = new net.minecraft.world.item.enchantment.Enchantment(null) {
- // TODO: 1.20.6: methods are final now and the values are provided by EnchantmentDefinition - would probably need to create a new one on reload and modify the existing enchantment
- @Override
- public int getMinLevel() {
- return script.script.minLevel;
- }
- @Override
- public int getMaxLevel() {
- return script.script.maxLevel;
- }
- @Override
- public int getMinCost(int level) {
- return script.script.getMinCost(level);
- }
- @Override
- public int getMaxCost(int level) {
- return script.script.getMaxCost(level);
- }
- @Override
- public int getDamageProtection(int level, DamageSource src) {
- return script.script.getDamageProtection(level, src.getMsgId(), src.getEntity() == null ? null : src.getEntity().getBukkitEntity());
- }
- // TODO: 1.20.6: Takes an EntityType now, and MobType seems to have been removed in favor of vanilla tags - can probably use these to backsupport & properly pass the entity type
- @Override
- public float getDamageBonus(int level, EntityType type) {
- String typeName = "UNDEFINED";
- if (type == MobType.ARTHROPOD) {
- typeName = "ARTHROPOD";
- }
- else if (type == MobType.ILLAGER) {
- typeName = "ILLAGER";
- }
- else if (type == MobType.UNDEAD) {
- typeName = "UNDEAD";
- }
- else if (type == MobType.WATER) {
- typeName = "WATER";
- }
- return script.script.getDamageBonus(level, typeName);
- }
- @Override
- protected boolean checkCompatibility(net.minecraft.world.item.enchantment.Enchantment nmsEnchantment) {
- ResourceLocation nmsKey = BuiltInRegistries.ENCHANTMENT.getKey(nmsEnchantment);
- NamespacedKey bukkitKey = CraftNamespacedKey.fromMinecraft(nmsKey);
- org.bukkit.enchantments.Enchantment bukkitEnchant = CraftEnchantment.getByKey(bukkitKey);
- return script.script.isCompatible(bukkitEnchant);
- }
- @Override
- protected String getOrCreateDescriptionId() {
- return script.script.descriptionId;
- }
- @Override
- public String getDescriptionId() {
- return script.script.descriptionId;
- }
- @Override
- public Component getFullname(int level) {
- return Handler.componentToNMS(script.script.getFullName(level));
- }
- @Override
- public boolean canEnchant(net.minecraft.world.item.ItemStack var0) {
- return super.canEnchant(var0) && script.script.canEnchant(CraftItemStack.asBukkitCopy(var0));
- }
- @Override
- public void doPostAttack(LivingEntity attacker, Entity victim, int level) {
- script.script.doPostAttack(attacker.getBukkitEntity(), victim.getBukkitEntity(), level);
+ // Rarity
+ Rarity rarity = Rarity.valueOf(script.script.rarity);
+ Enchantment.EnchantmentDefinition nmsEnchantmentDefinition = new Enchantment.EnchantmentDefinition(
+ new DynamicHolderSet<>(BuiltInRegistries.ITEM, nmsItem -> script.script.canEnchant(CraftItemStack.asNewCraftStack(nmsItem))),
+ Optional.of(EnchantmentCategory.valueOf(script.script.category).getItems()),
+ rarity.getWeight(), script.script.maxLevel,
+ tryParseCost(script.script, script.script.minCostTaggable, script.script::getMinCost),
+ tryParseCost(script.script, script.script.maxCostTaggable, script.script::getMaxCost),
+ rarity.getAnvilCost(), nmsSlotGroups);
+ // Damage bonus
+ DataComponentMap.Builder nmsEnchantmentEffectsBuilder = DataComponentMap.builder();
+ DynamicContextAwareLevelValue damageModifier = new DynamicContextAwareLevelValue((level, lootContext) ->
+ script.script.getDamageBonus(level, MobType.fromEntityType(lootContext.getParameter(LootContextParams.THIS_ENTITY).getType()).name())
+ );
+ addEnchantmentEffect(nmsEnchantmentEffectsBuilder, EnchantmentEffectComponents.DAMAGE, damageModifier);
+ // Damage protection
+ DynamicContextAwareLevelValue damageProtectionModifier = new DynamicContextAwareLevelValue((level, lootContext) -> {
+ DamageSource nmsDamageSource = lootContext.getParameter(LootContextParams.DAMAGE_SOURCE);
+ return (float) script.script.getDamageProtection(level, nmsDamageSource.getMsgId(), nmsDamageSource.getEntity() != null ? nmsDamageSource.getEntity().getBukkitEntity() : null);
+ });
+ addEnchantmentEffect(nmsEnchantmentEffectsBuilder, EnchantmentEffectComponents.DAMAGE_PROTECTION, damageProtectionModifier);
+ // Full name
+ String taggedNoLevel = TagManager.tag(script.script.fullNameTaggable.replace(" ", "").replace("", ""), DenizenCore.implementation.getTagContext(script.script));
+ Component nmsFullName = Handler.componentToNMS(FormattedTextHelper.parse(taggedNoLevel, ChatColor.GRAY));
+ // Compatible enchantments
+ HolderSet nmsIncompatibleEnchants = new DynamicHolderSet<>(nmsEnchantmentRegistry, nmsEnchantment -> !script.script.isCompatible(CraftEnchantment.minecraftToBukkit(nmsEnchantment)));
+ // Registration
+ Enchantment nmsEnchantment = new Enchantment(nmsFullName, nmsEnchantmentDefinition, nmsIncompatibleEnchants, nmsEnchantmentEffectsBuilder.build());
+ ResourceLocation nmsEnchantmentKey = CraftNamespacedKey.toMinecraft(script.script.getKey());
+ Holder.Reference nmsExistingEnchantment = nmsEnchantmentRegistry.get(nmsEnchantmentKey).orElse(null);
+ boolean wasFrozen = REGISTRY_FROZEN.getBoolean(nmsEnchantmentRegistry);
+ REGISTRY_FROZEN.setBoolean(nmsEnchantmentRegistry, false);
+ if (nmsExistingEnchantment == null) {
+ Map, ?> nmsRegistryHolders = (Map, ?>) REGISTRY_INTRUSIVE_HOLDERS.get(nmsEnchantmentRegistry);
+ if (nmsRegistryHolders == null) {
+ REGISTRY_INTRUSIVE_HOLDERS.set(nmsEnchantmentRegistry, new IdentityHashMap<>());
}
- @Override
- public void doPostHurt(LivingEntity victim, Entity attacker, int level) {
- script.script.doPostHurt(victim.getBukkitEntity(), attacker.getBukkitEntity(), level);
- }
- @Override
- public boolean isTreasureOnly() {
- return script.script.isTreasureOnly;
- }
- @Override
- public boolean isCurse() {
- return script.script.isCurse;
- }
- @Override
- public boolean isTradeable() {
- return script.script.isTradable;
- }
- @Override
- public boolean isDiscoverable() {
- return script.script.isDiscoverable;
- }
- };
- NamespacedKey enchantmentKey = new NamespacedKey(Denizen.getInstance(), script.script.id);
- Registry.register(BuiltInRegistries.ENCHANTMENT, enchantmentKey.toString(), nmsEnchant);
- String enchName = CoreUtilities.toUpperCase(script.script.id);
- CraftEnchantment ench = new CraftEnchantment(enchantmentKey, nmsEnchant) {
- @Override
- public String getName() {
- return enchName;
- }
- };
- REGISTRY_INTRUSIVE_HOLDERS.set(BuiltInRegistries.ENCHANTMENT, holders);
+ nmsEnchantmentRegistry.createIntrusiveHolder(nmsEnchantment);
+ nmsEnchantmentRegistry.register(ResourceKey.create(Registries.ENCHANTMENT, nmsEnchantmentKey), nmsEnchantment, NO_NETWORKING_REGISTRATION_INFO);
+ REGISTRY_INTRUSIVE_HOLDERS.set(nmsEnchantmentRegistry, nmsRegistryHolders);
+ }
+ else {
+ replaceRegistryValue(nmsEnchantmentRegistry, nmsEnchantment, nmsExistingEnchantment, nmsEnchantmentKey);
+ }
if (wasFrozen) {
- BuiltInRegistries.ENCHANTMENT.freeze();
+ Object nmsTagSet = REGISTRY_ALL_TAGS.get(nmsEnchantmentRegistry);
+ REGISTRY_ALL_TAGS.set(nmsEnchantmentRegistry, MAPPED_REGISTRY_TAG_SET_UNBOUND.invoke());
+ nmsEnchantmentRegistry.freeze();
+ REGISTRY_ALL_TAGS.set(nmsEnchantmentRegistry, nmsTagSet);
}
- return ench;
+ return CraftEnchantment.minecraftToBukkit(nmsEnchantment);
}
- catch (Throwable ex) {
- Debug.echoError("Failed to register enchantment " + script.script.id);
- Debug.echoError(ex);
+ catch (Throwable e) {
+ Debug.echoError(e);
return null;
}
}
- // TODO: 1.20.6: rarity is just an int now (weight), can deprecate & backsupport by estimating it based on the weight
+ private static void addEnchantmentEffect(DataComponentMap.Builder nmsEffects, DataComponentType
>> nmsEffectType, DynamicContextAwareLevelValue value) {
+ nmsEffects.set(nmsEffectType, List.of(new ConditionalEffect<>(new AddValue(value), Optional.of(value))));
+ }
+
+ public static final Field MAPPED_REGISTRY_BY_VALUE = ReflectionHelper.getFields(MappedRegistry.class).get("byValue");
+ public static final Field MAPPED_REGISTRY_TO_ID = ReflectionHelper.getFields(MappedRegistry.class).get("toId");
+ public static final Field MAPPED_REGISTRY_REGISTRATION_INFOS = ReflectionHelper.getFields(MappedRegistry.class).get("registrationInfos");
+ public static final Field HOLDER_REFERENCE_VALUE = ReflectionHelper.getFields(Holder.Reference.class).get("value");
+
+ public void replaceRegistryValue(MappedRegistry nmsRegistry, Enchantment nmsNewValue, Holder.Reference nmsExistingHolder, ResourceLocation nmsKey) throws IllegalAccessException {
+ Enchantment nmsOldValue = nmsExistingHolder.value();
+ HOLDER_REFERENCE_VALUE.set(nmsExistingHolder, nmsNewValue);
+ Map> byValue = (Map>) MAPPED_REGISTRY_BY_VALUE.get(nmsRegistry);
+ byValue.remove(nmsOldValue);
+ byValue.put(nmsNewValue, nmsExistingHolder);
+ Reference2IntMap toId = (Reference2IntMap) MAPPED_REGISTRY_TO_ID.get(nmsRegistry);
+ toId.put(nmsNewValue, toId.removeInt(nmsOldValue));
+ ResourceKey nmsResourceKey = ResourceKey.create(Registries.ENCHANTMENT, nmsKey);
+ ((Map, RegistrationInfo>) MAPPED_REGISTRY_REGISTRATION_INFOS.get(nmsRegistry)).put(nmsResourceKey, NO_NETWORKING_REGISTRATION_INFO);
+ }
+
@Override
- public String getRarity(Enchantment enchantment) {
- return ((CraftEnchantment) enchantment).getHandle().getRarity().name();
+ public String getRarity(org.bukkit.enchantments.Enchantment enchantment) {
+ return Rarity.fromWeight(CraftEnchantment.bukkitToMinecraft(enchantment).getWeight()).name();
}
@Override
- public boolean isDiscoverable(Enchantment enchantment) {
- return ((CraftEnchantment) enchantment).getHandle().isDiscoverable();
+ public boolean isDiscoverable(org.bukkit.enchantments.Enchantment enchantment) {
+ return CraftEnchantment.bukkitToMinecraftHolder(enchantment).is(EnchantmentTags.IN_ENCHANTING_TABLE);
}
@Override
- public boolean isTradable(Enchantment enchantment) {
- return ((CraftEnchantment) enchantment).getHandle().isTradeable();
+ public boolean isTradable(org.bukkit.enchantments.Enchantment enchantment) {
+ return CraftEnchantment.bukkitToMinecraftHolder(enchantment).is(EnchantmentTags.TRADEABLE);
}
@Override
- public boolean isCurse(Enchantment enchantment) {
- return ((CraftEnchantment) enchantment).getHandle().isCurse();
+ public boolean isCurse(org.bukkit.enchantments.Enchantment enchantment) {
+ return CraftEnchantment.bukkitToMinecraftHolder(enchantment).is(EnchantmentTags.CURSE);
}
@Override
- public int getMinCost(Enchantment enchantment, int level) {
- return ((CraftEnchantment) enchantment).getHandle().getMinCost(level);
+ public int getMinCost(org.bukkit.enchantments.Enchantment enchantment, int level) {
+ return CraftEnchantment.bukkitToMinecraft(enchantment).getMinCost(level);
}
@Override
- public int getMaxCost(Enchantment enchantment, int level) {
- return ((CraftEnchantment) enchantment).getHandle().getMaxCost(level);
+ public int getMaxCost(org.bukkit.enchantments.Enchantment enchantment, int level) {
+ return CraftEnchantment.bukkitToMinecraft(enchantment).getMaxCost(level);
}
@Override
- public String getFullName(Enchantment enchantment, int level) {
- return FormattedTextHelper.stringify(Handler.componentToSpigot(((CraftEnchantment) enchantment).getHandle().getFullname(level)));
+ public String getFullName(org.bukkit.enchantments.Enchantment enchantment, int level) {
+ return FormattedTextHelper.stringify(Handler.componentToSpigot(Enchantment.getFullname(CraftEnchantment.bukkitToMinecraftHolder(enchantment), level)));
}
- // TODO: 1.20.6: MobType was removed in favor of using the entity type directly - deprecate + potentially backsupport with vanilla tags
@Override
- public float getDamageBonus(Enchantment enchantment, int level, String type) {
- MobType mobType = switch (type) {
- case "illager" -> MobType.ILLAGER;
- case "undead" -> MobType.UNDEAD;
- case "water" -> MobType.WATER;
- case "arthropod" -> MobType.ARTHROPOD;
- default -> MobType.UNDEFINED;
- };
- return ((CraftEnchantment) enchantment).getHandle().getDamageBonus(level, mobType);
+ public float getDamageBonus(org.bukkit.enchantments.Enchantment enchantment, int level, String type) {
+ TagKey> nmsEntityTag = MobType.valueOf(CoreUtilities.toUpperCase(type)).getNmsTagKey();
+ EntityType> nmsEntityType = Optional.ofNullable(nmsEntityTag)
+ .flatMap(BuiltInRegistries.ENTITY_TYPE::get)
+ .map(holders -> holders.get(0).value())
+ .orElse((EntityType) EntityType.PIG);
+ ServerLevel nmsWorld = MinecraftServer.getServer().overworld();
+ MutableFloat damage = new MutableFloat(2);
+ CraftEnchantment.bukkitToMinecraft(enchantment).modifyDamage(nmsWorld, level, new ItemStack(Items.AIR),
+ mockEntity(nmsEntityType, nmsWorld), nmsWorld.damageSources().generic(), damage);
+ return Math.max(damage.floatValue() - 2, 0);
}
@Override
- public int getDamageProtection(Enchantment enchantment, int level, EntityDamageEvent.DamageCause type, org.bukkit.entity.Entity attacker) {
+ public int getDamageProtection(org.bukkit.enchantments.Enchantment enchantment, int level, EntityDamageEvent.DamageCause type, org.bukkit.entity.Entity attacker) {
Entity nmsAttacker = attacker == null ? null : ((CraftEntity) attacker).getHandle();
- DamageSource src = EntityHelperImpl.getSourceFor(nmsAttacker, type, nmsAttacker);
- if (src instanceof EntityHelperImpl.FakeDamageSrc fakeDamageSrc) {
- src = fakeDamageSrc.real;
+ DamageSource nmsDamageSource = EntityHelperImpl.getSourceFor(nmsAttacker, type, nmsAttacker);
+ if (nmsDamageSource instanceof EntityHelperImpl.FakeDamageSrc fakeDamageSrc) {
+ nmsDamageSource = fakeDamageSrc.real;
+ }
+ ServerLevel nmsWorld = MinecraftServer.getServer().overworld();
+ MutableFloat damageProtection = new MutableFloat(0);
+ CraftEnchantment.bukkitToMinecraft(enchantment).modifyDamageProtection(nmsWorld, level, new ItemStack(Items.AIR),
+ nmsAttacker != null ? nmsAttacker : mockEntity(EntityType.ZOMBIE, nmsWorld), nmsDamageSource, damageProtection);
+ return Math.max(Math.round(damageProtection.floatValue()), 0);
+ }
+
+ private static T mockEntity(EntityType nmsEntityType, ServerLevel nmsWorld) {
+ return nmsEntityType.create(nmsWorld, EntitySpawnReason.COMMAND);
+ }
+
+ //////////////////
+ /// Networking ///
+ //////////////////
+
+ public static final Class> CHANNEL_INITIALIZE_LISTENER_CLASS = ReflectionHelper.getClassOrThrow("io.papermc.paper.network.ChannelInitializeListener");
+ public static final MethodHandle ADD_CHANNEL_INITIALIZE_LISTENER = ReflectionHelper.getMethodHandle(
+ ReflectionHelper.getClassOrThrow("io.papermc.paper.network.ChannelInitializeListenerHolder"),
+ "addListener",
+ ReflectionHelper.getClassOrThrow("net.kyori.adventure.key.Key"), CHANNEL_INITIALIZE_LISTENER_CLASS
+ );
+
+ public static boolean initialized = false;
+
+ public static void verifyNetworkingInitialized() {
+ if (initialized) {
+ return;
+ }
+ Object handler = ReflectionHelper.getStaticLambda(CHANNEL_INITIALIZE_LISTENER_CLASS, "afterInitChannel", EnchantmentExcludingChannelHandler.class, "injectSelf");
+ try {
+ ADD_CHANNEL_INITIALIZE_LISTENER.invoke(null, handler);
+ }
+ catch (Throwable e) {
+ Debug.echoError(e);
+ }
+ initialized = true;
+ }
+
+ public static class EnchantmentExcludingChannelHandler extends ChannelOutboundHandlerAdapter {
+
+ public static void injectSelf(Channel channel) {
+ channel.pipeline().addBefore("packet_handler", "denizen_enchant_excluder", new EnchantmentExcludingChannelHandler());
+ }
+
+ // NOTE: This runs on Netty IO threads (i.e. async)
+ @Override
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
+ if (msg instanceof ClientboundRegistryDataPacket registryDataPacket && registryDataPacket.registry() == Registries.ENCHANTMENT) {
+ List registryEntries = new ArrayList<>(registryDataPacket.entries().size());
+ boolean anyRemoved = false;
+ for (RegistrySynchronization.PackedRegistryEntry registryEntry : registryDataPacket.entries()) {
+ if (registryEntry.id().getNamespace().equals("denizen") && EnchantmentScriptContainer.registeredEnchantmentContainers.containsKey(registryEntry.id().getPath())) {
+ anyRemoved = true;
+ continue;
+ }
+ registryEntries.add(registryEntry);
+ }
+ if (anyRemoved) {
+ msg = new ClientboundRegistryDataPacket(registryDataPacket.registry(), registryEntries);
+ }
+ }
+ super.write(ctx, msg, promise);
}
- return ((CraftEnchantment) enchantment).getHandle().getDamageProtection(level, src);
}
- */
}
From 21c87d64a9f8db307955710251f785b82a595112 Mon Sep 17 00:00:00 2001
From: Aya <31237389+tal5@users.noreply.github.com>
Date: Sun, 29 Jun 2025 23:58:12 +0100
Subject: [PATCH 2/3] Cleanup
---
.../containers/core/EnchantmentScriptContainer.java | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/core/EnchantmentScriptContainer.java b/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/core/EnchantmentScriptContainer.java
index 4db510d2ca..ab45119c5a 100644
--- a/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/core/EnchantmentScriptContainer.java
+++ b/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/core/EnchantmentScriptContainer.java
@@ -9,14 +9,12 @@
import com.denizenscript.denizen.utilities.BukkitImplDeprecations;
import com.denizenscript.denizen.utilities.FormattedTextHelper;
import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
-import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.objects.core.ScriptTag;
import com.denizenscript.denizencore.scripts.ScriptEntry;
import com.denizenscript.denizencore.scripts.containers.ScriptContainer;
import com.denizenscript.denizencore.scripts.queues.ContextSource;
import com.denizenscript.denizencore.scripts.queues.core.InstantQueue;
-import com.denizenscript.denizencore.tags.ParseableTag;
import com.denizenscript.denizencore.tags.TagContext;
import com.denizenscript.denizencore.tags.TagManager;
import com.denizenscript.denizencore.utilities.AsciiMatcher;
@@ -271,16 +269,6 @@ public String autoTag(String value, ContextSource src) {
return TagManager.tag(value, context);
}
- public ObjectTag autoTag(ParseableTag tag, ContextSource src) {
- if (tag == null) {
- return null;
- }
- validateThread();
- TagContext context = new BukkitTagContext(null, new ScriptTag(this));
- context.contextSource = src;
- return tag.parse(context);
- }
-
public String autoTagForLevel(String value, int level) {
ContextSource.SimpleMap src = new ContextSource.SimpleMap();
src.contexts = new HashMap<>();
From 5dd116ddeaf4c01ffac74b10540c8768bdc33e78 Mon Sep 17 00:00:00 2001
From: Aya <31237389+tal5@users.noreply.github.com>
Date: Mon, 30 Jun 2025 00:56:52 +0100
Subject: [PATCH 3/3] Fix version check
---
.../denizen/scripts/containers/ContainerRegistry.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/ContainerRegistry.java b/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/ContainerRegistry.java
index 5e152d1548..d7ce204188 100644
--- a/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/ContainerRegistry.java
+++ b/plugin/src/main/java/com/denizenscript/denizen/scripts/containers/ContainerRegistry.java
@@ -15,7 +15,7 @@ public static void registerMainContainers() {
if (Depends.vault != null) {
ScriptRegistry._registerType("economy", EconomyScriptContainer.class);
}
- if (NMSHandler.getVersion().isAtMost(NMSVersion.v1_20) || Depends.denizenEnchantmentFix != null) {
+ if (NMSHandler.getVersion().isAtMost(NMSVersion.v1_19) || Depends.denizenEnchantmentFix != null) {
ScriptRegistry._registerType("enchantment", EnchantmentScriptContainer.class);
}
ScriptRegistry._registerType("entity", EntityScriptContainer.class);