From 9ae84a1a7477ab493da03080eedceb5449fe6fc4 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sun, 12 Jul 2026 15:39:49 +0200 Subject: [PATCH 01/20] re-implement hide-flags param for engine.yml (lesser revert of 00d677f) --- src/main/java/studio/magemonkey/divinity/config/EngineCfg.java | 2 ++ .../divinity/manager/listener/object/ItemUpdaterListener.java | 3 ++- src/main/resources/engine.yml | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java index 5757107d..38919a5e 100644 --- a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java +++ b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java @@ -46,6 +46,7 @@ public EngineCfg(@NotNull Divinity plugin) throws InvalidConfigurationException public static boolean ATTRIBUTES_EFFECTIVE_FOR_MOBS; public static boolean ATTRIBUTES_EFFECTIVE_IN_OFFHAND; public static boolean ATTRIBUTES_ALLOW_HOLD_REQUIREMENTS; + public static boolean ATTRIBUTES_HIDE_FLAGS; public static boolean ATTRIBUTES_DURABILITY_BREAK_ITEMS; public static boolean ATTRIBUTES_DURABILITY_REDUCE_FOR_MOBS; @@ -177,6 +178,7 @@ public void setup() { EngineCfg.ATTRIBUTES_EFFECTIVE_FOR_MOBS = cfg.getBoolean(path + "effective-for-mobs"); EngineCfg.ATTRIBUTES_EFFECTIVE_IN_OFFHAND = cfg.getBoolean(path + "effective-in-offhand"); EngineCfg.ATTRIBUTES_ALLOW_HOLD_REQUIREMENTS = cfg.getBoolean(path + "allow-hold-items-you-cant-use"); + EngineCfg.ATTRIBUTES_HIDE_FLAGS = cfg.getBoolean(path + "hide-flags"); path = "attributes.durability."; EngineCfg.ATTRIBUTES_DURABILITY_BREAK_ITEMS = cfg.getBoolean(path + "break-items-on-zero"); diff --git a/src/main/java/studio/magemonkey/divinity/manager/listener/object/ItemUpdaterListener.java b/src/main/java/studio/magemonkey/divinity/manager/listener/object/ItemUpdaterListener.java index b21b19e9..0839069e 100644 --- a/src/main/java/studio/magemonkey/divinity/manager/listener/object/ItemUpdaterListener.java +++ b/src/main/java/studio/magemonkey/divinity/manager/listener/object/ItemUpdaterListener.java @@ -25,6 +25,7 @@ import studio.magemonkey.codex.manager.IListener; import studio.magemonkey.codex.util.DataUT; import studio.magemonkey.divinity.Divinity; +import studio.magemonkey.divinity.config.EngineCfg; import studio.magemonkey.divinity.stats.items.ItemStats; public class ItemUpdaterListener extends IListener { @@ -106,7 +107,7 @@ public void update(ItemStack item, @Nullable Player player) { if (fixed) { DataUT.removeData(item, key); meta.removeItemFlags(ItemFlag.HIDE_ATTRIBUTES); - } else { + } else if(EngineCfg.ATTRIBUTES_HIDE_FLAGS) { meta.addItemFlags(ItemFlag.values()); } item.setItemMeta(meta); diff --git a/src/main/resources/engine.yml b/src/main/resources/engine.yml index 81ca70bf..8f1ad776 100644 --- a/src/main/resources/engine.yml +++ b/src/main/resources/engine.yml @@ -63,6 +63,8 @@ attributes: # When enabled, allows to hold in hand items with requirements that player don't meet. # Even when this is 'true', item attributes won't be applied to a player until he meet the requirements. allow-hold-items-you-cant-use: false + # When enabled, hides attributes and enchantments by default on all custom items. + hide-flags: true combat: # Whether to use the old combat formula for calculating defenses From 93a0cf1df0e6c7ae8703a13f6dc797c49b24ee01 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sun, 12 Jul 2026 16:04:41 +0200 Subject: [PATCH 02/20] added backward/restore of original flags of an item --- .../magemonkey/divinity/config/EngineCfg.java | 2 +- .../listener/object/ItemUpdaterListener.java | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java index 38919a5e..a10a29e5 100644 --- a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java +++ b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java @@ -175,11 +175,11 @@ public void setup() { // A T T R I B U T E S // path = "attributes."; + cfg.addMissing(path + "hide-flags", true); EngineCfg.ATTRIBUTES_EFFECTIVE_FOR_MOBS = cfg.getBoolean(path + "effective-for-mobs"); EngineCfg.ATTRIBUTES_EFFECTIVE_IN_OFFHAND = cfg.getBoolean(path + "effective-in-offhand"); EngineCfg.ATTRIBUTES_ALLOW_HOLD_REQUIREMENTS = cfg.getBoolean(path + "allow-hold-items-you-cant-use"); EngineCfg.ATTRIBUTES_HIDE_FLAGS = cfg.getBoolean(path + "hide-flags"); - path = "attributes.durability."; EngineCfg.ATTRIBUTES_DURABILITY_BREAK_ITEMS = cfg.getBoolean(path + "break-items-on-zero"); EngineCfg.ATTRIBUTES_DURABILITY_REDUCE_FOR_MOBS = cfg.getBoolean(path + "effective-for.mobs"); diff --git a/src/main/java/studio/magemonkey/divinity/manager/listener/object/ItemUpdaterListener.java b/src/main/java/studio/magemonkey/divinity/manager/listener/object/ItemUpdaterListener.java index 0839069e..ab8a41d6 100644 --- a/src/main/java/studio/magemonkey/divinity/manager/listener/object/ItemUpdaterListener.java +++ b/src/main/java/studio/magemonkey/divinity/manager/listener/object/ItemUpdaterListener.java @@ -25,9 +25,12 @@ import studio.magemonkey.codex.manager.IListener; import studio.magemonkey.codex.util.DataUT; import studio.magemonkey.divinity.Divinity; +import studio.magemonkey.divinity.api.DivinityAPI; import studio.magemonkey.divinity.config.EngineCfg; import studio.magemonkey.divinity.stats.items.ItemStats; +import java.util.Objects; + public class ItemUpdaterListener extends IListener { @@ -109,6 +112,18 @@ public void update(ItemStack item, @Nullable Player player) { meta.removeItemFlags(ItemFlag.HIDE_ATTRIBUTES); } else if(EngineCfg.ATTRIBUTES_HIDE_FLAGS) { meta.addItemFlags(ItemFlag.values()); + } else { + // Reapply only the required flags on from a copied item + try { + var itemCopy = Objects.requireNonNull(CodexEngine.get().getItemManager().getMainItemType(item)).create(); + if(itemCopy == null) return; + var itemCopyMeta = itemCopy.getItemMeta(); + if(itemCopyMeta == null) return; + meta.removeItemFlags(ItemFlag.values()); + meta.addItemFlags(itemCopyMeta.getItemFlags().toArray(new ItemFlag[0])); + } catch (Exception e) { + return; + } } item.setItemMeta(meta); } From 1325fe91bf30f26494c9cf5bf27a964778919128 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:36:35 +0200 Subject: [PATCH 03/20] restored legacy vanilla configurations with more seperated configurations --- .../studio/magemonkey/divinity/config/EngineCfg.java | 11 +++++++++++ .../magemonkey/divinity/manager/EntityManager.java | 9 +++++++++ .../manager/listener/object/DynamicStatListener.java | 2 ++ .../studio/magemonkey/divinity/stats/EntityStats.java | 1 + .../magemonkey/divinity/stats/items/ItemStats.java | 3 +++ 5 files changed, 26 insertions(+) diff --git a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java index a10a29e5..3f71b5d7 100644 --- a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java +++ b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java @@ -114,6 +114,9 @@ public EngineCfg(@NotNull Divinity plugin) throws InvalidConfigurationException public static String LORE_STYLE_FABLED_ATTRIBUTE_FORMAT; + public static boolean LEGACY_VANILLA_ENTITY_STATS; + public static boolean LEGACY_VANILLA_ITEM_STATS; + public void setup() { this.plugin.info("Loading engine configuration..."); @@ -426,6 +429,14 @@ public void setup() { EngineCfg.LORE_STYLE_FABLED_ATTRIBUTE_FORMAT = StringUT.color(cfg.getString(path, "&7%attrPre%&3%name%&7%attrPost%")); + + path = "legacy.vanilla-entity-stats"; + cfg.addMissing(path, false); + EngineCfg.LEGACY_VANILLA_ENTITY_STATS = cfg.getBoolean(path, false); + path = "legacy.vanilla-item-stats"; + cfg.addMissing(path, false); + EngineCfg.LEGACY_VANILLA_ITEM_STATS = cfg.getBoolean(path, false); + cfg.saveChanges(); } diff --git a/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java b/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java index ddc45f9f..5e0bf4dc 100644 --- a/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java +++ b/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java @@ -19,6 +19,7 @@ import studio.magemonkey.divinity.api.event.DivinityDamageEvent; import studio.magemonkey.divinity.api.event.EntityDivinityItemPickupEvent; import studio.magemonkey.divinity.api.event.EntityEquipmentChangeEvent; +import studio.magemonkey.divinity.config.EngineCfg; import studio.magemonkey.divinity.modules.api.QModuleDrop; import studio.magemonkey.divinity.stats.EntityStats; import studio.magemonkey.divinity.stats.EntityStatsTask; @@ -90,6 +91,7 @@ public void shutdown() { @EventHandler(priority = EventPriority.MONITOR) public void onStatsDeath(EntityDeathEvent e) { + if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; LivingEntity entity = e.getEntity(); previousEquipment.remove(e.getEntity().getUniqueId()); EntityStats.get(entity).handleDeath(); @@ -98,22 +100,26 @@ public void onStatsDeath(EntityDeathEvent e) { // Clear stats on player exit @EventHandler(priority = EventPriority.HIGHEST) public void onStatsQuit(PlayerQuitEvent e) { + if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; EntityStats.purge(e.getPlayer()); } @EventHandler(priority = EventPriority.HIGHEST) public void onStatsJoin(PlayerJoinEvent e) { + if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; EntityStats.get(e.getPlayer()); this.pushToUpdate(e.getPlayer(), 1D); } @EventHandler public void quit(PlayerQuitEvent event) { + if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; previousEquipment.remove(event.getPlayer().getUniqueId()); } @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onStatsRegen(EntityRegainHealthEvent e) { + if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; Entity e1 = e.getEntity(); if (!(e1 instanceof LivingEntity)) return; @@ -124,6 +130,7 @@ public void onStatsRegen(EntityRegainHealthEvent e) { @EventHandler(ignoreCancelled = true) public void onPickup(EntityPickupItemEvent e) { + if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; if (!ProjectileStats.isPickable(e.getItem())) { e.setCancelled(true); } @@ -138,6 +145,7 @@ public void onPickup(EntityPickupItemEvent e) { } private final void pushToUpdate(@NotNull LivingEntity entity, double time) { + if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; EntityEquipment equip = new EntityEquipmentSnapshot(entity); previousEquipment.put(entity.getUniqueId(), equip); if (time <= 0D) { @@ -153,6 +161,7 @@ public void run() { } private final void addDuplicatorFixer(@NotNull Entity entity) { + if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; entity.setMetadata(PACKET_DUPLICATOR_FIXER, new FixedMetadataValue(plugin, "fixed")); } diff --git a/src/main/java/studio/magemonkey/divinity/manager/listener/object/DynamicStatListener.java b/src/main/java/studio/magemonkey/divinity/manager/listener/object/DynamicStatListener.java index 2c2f021b..5b1d082e 100644 --- a/src/main/java/studio/magemonkey/divinity/manager/listener/object/DynamicStatListener.java +++ b/src/main/java/studio/magemonkey/divinity/manager/listener/object/DynamicStatListener.java @@ -13,6 +13,7 @@ import org.jetbrains.annotations.Nullable; import studio.magemonkey.codex.manager.IListener; import studio.magemonkey.divinity.Divinity; +import studio.magemonkey.divinity.config.EngineCfg; import studio.magemonkey.divinity.stats.items.ItemStats; import studio.magemonkey.divinity.stats.items.api.DynamicStat; @@ -27,6 +28,7 @@ public DynamicStatListener(@NotNull Divinity plugin) { } public static void updateItem(@Nullable Player p, @NotNull ItemStack item) { + if(EngineCfg.LEGACY_VANILLA_ITEM_STATS) return; for (DynamicStat dynamicStat : ItemStats.getDynamicStats()) { dynamicStat.updateItem(p, item); } diff --git a/src/main/java/studio/magemonkey/divinity/stats/EntityStats.java b/src/main/java/studio/magemonkey/divinity/stats/EntityStats.java index 5320a32a..f7b29b2e 100644 --- a/src/main/java/studio/magemonkey/divinity/stats/EntityStats.java +++ b/src/main/java/studio/magemonkey/divinity/stats/EntityStats.java @@ -438,6 +438,7 @@ private void updateInventory() { } public void updateAll() { + if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; if (!EngineCfg.ATTRIBUTES_EFFECTIVE_FOR_MOBS && !this.isPlayer()) { return; } diff --git a/src/main/java/studio/magemonkey/divinity/stats/items/ItemStats.java b/src/main/java/studio/magemonkey/divinity/stats/items/ItemStats.java index 0faa6fb2..51e15b6a 100644 --- a/src/main/java/studio/magemonkey/divinity/stats/items/ItemStats.java +++ b/src/main/java/studio/magemonkey/divinity/stats/items/ItemStats.java @@ -20,6 +20,7 @@ import studio.magemonkey.codex.modules.IModule; import studio.magemonkey.codex.util.DataUT; import studio.magemonkey.divinity.Divinity; +import studio.magemonkey.divinity.config.EngineCfg; import studio.magemonkey.divinity.modules.api.QModuleDrop; import studio.magemonkey.divinity.stats.items.api.DuplicableItemLoreStat; import studio.magemonkey.divinity.stats.items.api.DynamicStat; @@ -308,6 +309,8 @@ public static boolean hasStat(@NotNull ItemStack item, @Nullable Player player, // ----------------------------------------------------------------- // public static void updateVanillaAttributes(@NotNull ItemStack item, @Nullable Player player) { + if (EngineCfg.LEGACY_VANILLA_ITEM_STATS) return; + addAttribute(item, player, NBTAttribute.MAX_HEALTH, getStat(item, player, TypedStat.Type.MAX_HEALTH)); addAttribute(item, player, NBTAttribute.MOVEMENT_SPEED, getStat(item, player, TypedStat.Type.MOVEMENT_SPEED)); addAttribute(item, player, NBTAttribute.ATTACK_SPEED, getStat(item, player, TypedStat.Type.ATTACK_SPEED)); From 6182a3ed7b0ea0164e3bd65b6b8ac0dcc27700e4 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Fri, 24 Jul 2026 20:09:04 +0200 Subject: [PATCH 04/20] fixed resistance and absorption behaviour --- .../listener/object/VanillaWrapperListener.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListener.java b/src/main/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListener.java index e609e643..07e9df8d 100644 --- a/src/main/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListener.java +++ b/src/main/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListener.java @@ -376,16 +376,23 @@ public void onVanillaDamage(EntityDamageEvent e) { // +----------------------------------------------------+ // Divinity.getInstance().info("Damage Final Check: " + e.getFinalDamage() + "/" + e.getDamage()); if (e.getFinalDamage() != e.getDamage()) { - double absorption = Math.min(e.getDamage(), victim.getAbsorptionAmount()); + double damageBeforeAbsorption = e.getDamage(); + if (e.isApplicable(DamageModifier.RESISTANCE)) { + damageBeforeAbsorption += e.getDamage(DamageModifier.RESISTANCE); + } + double absorption = Math.min(Math.max(0D, damageBeforeAbsorption), victim.getAbsorptionAmount()); for (DamageModifier dmgModifier : DamageModifier.values()) { - if (dmgModifier == DamageModifier.ABSORPTION) continue; if (e.isApplicable(dmgModifier)) { if (dmgModifier == DamageModifier.BASE) { // Divinity.getInstance().info("FINAL - " + dmgModifier.name() + ": " + e.getDamage()); - e.setDamage(dmgModifier, e.getDamage() - absorption); + e.setDamage(dmgModifier, e.getDamage()); } else if (dmgModifier == DamageModifier.ABSORPTION) { - e.setDamage(dmgModifier, absorption); - } else if (!dmgModifier.name().equals("INVULNERABILITY_REDUCTION")) + // Bukkit represents absorption as a negative damage modifier. + // Keep it in the final damage calculation instead of subtracting + // it from BASE (which caused the absorption modifier to be applied + // twice or not at all depending on the damage source). + e.setDamage(dmgModifier, -absorption); + } else if (!dmgModifier.name().equals("INVULNERABILITY_REDUCTION") && !dmgModifier.name().equals("RESISTANCE")) e.setDamage(dmgModifier, 0); // Fix } } From 6315aee102270bb96eca3f66688c904f3547d7a0 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Fri, 24 Jul 2026 20:31:00 +0200 Subject: [PATCH 05/20] preserve vanilla attributes before stats --- .../divinity/manager/EntityManager.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java b/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java index 5e0bf4dc..125ae97b 100644 --- a/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java +++ b/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java @@ -2,6 +2,7 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.CreatureSpawnEvent; @@ -160,6 +161,23 @@ public void run() { }.runTask(Divinity.getInstance()); } + private void updateVanillaItemAttributes(@NotNull LivingEntity entity) { + if (EngineCfg.LEGACY_VANILLA_ITEM_STATS) return; + + EntityEquipment equipment = entity.getEquipment(); + if (equipment == null) return; + + for (ItemStack item : equipment.getArmorContents()) { + if (item != null) ItemStats.updateVanillaAttributes(item, entity instanceof Player ? (Player) entity : null); + } + + ItemStack main = equipment.getItemInMainHand(); + if (main != null) ItemStats.updateVanillaAttributes(main, entity instanceof Player ? (Player) entity : null); + + ItemStack off = equipment.getItemInOffHand(); + if (off != null) ItemStats.updateVanillaAttributes(off, entity instanceof Player ? (Player) entity : null); + } + private final void addDuplicatorFixer(@NotNull Entity entity) { if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; entity.setMetadata(PACKET_DUPLICATOR_FIXER, new FixedMetadataValue(plugin, "fixed")); @@ -206,6 +224,7 @@ public void onStatsUpdatePlayerHeld(PlayerItemHeldEvent e) { @EventHandler(priority = EventPriority.NORMAL) public void onEntityUpdateEquipmentChange(EntityEquipmentChangeEvent e) { + this.updateVanillaItemAttributes(e.getEntity()); this.pushToUpdate(e.getEntity(), 0.5D); } } From aa6641aeacd6d06eebc6ec3d9f2e79637eaea020 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 25 Jul 2026 01:19:26 +0000 Subject: [PATCH 06/20] Add tests for resistance/absorption fix and legacy vanilla flags Covers the changes in PR #339: unit tests exercising the VanillaWrapperListener damage-modifier math directly (resistance no longer zeroed out, absorption capped to the victim's available amount and applied only once), plus tests confirming the new LEGACY_VANILLA_ENTITY_STATS and LEGACY_VANILLA_ITEM_STATS flags correctly no-op the entity/item stat pipelines when enabled. --- .../divinity/manager/EntityManagerTest.java | 46 ++++++++ .../object/VanillaWrapperListenerTest.java | 104 ++++++++++++++++++ .../divinity/stats/items/ItemStatsTest.java | 40 +++++++ 3 files changed, 190 insertions(+) create mode 100644 src/test/java/studio/magemonkey/divinity/manager/EntityManagerTest.java create mode 100644 src/test/java/studio/magemonkey/divinity/stats/items/ItemStatsTest.java diff --git a/src/test/java/studio/magemonkey/divinity/manager/EntityManagerTest.java b/src/test/java/studio/magemonkey/divinity/manager/EntityManagerTest.java new file mode 100644 index 00000000..07e070bf --- /dev/null +++ b/src/test/java/studio/magemonkey/divinity/manager/EntityManagerTest.java @@ -0,0 +1,46 @@ +package studio.magemonkey.divinity.manager; + +import org.bukkit.event.player.PlayerToggleSprintEvent; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockbukkit.mockbukkit.entity.PlayerMock; +import studio.magemonkey.divinity.config.EngineCfg; +import studio.magemonkey.divinity.testutil.MockedTest; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class EntityManagerTest extends MockedTest { + private PlayerMock player; + + @BeforeEach + void setup() { + player = genPlayer("Travja"); + } + + @AfterEach + void resetFlag() { + EngineCfg.LEGACY_VANILLA_ENTITY_STATS = false; + } + + @Test + void entityStatsHandlingAppliesDuplicatorFixerByDefault() { + EngineCfg.LEGACY_VANILLA_ENTITY_STATS = false; + + server.getPluginManager().callEvent(new PlayerToggleSprintEvent(player, true)); + + assertTrue(EntityManager.isPacketDuplicatorFixed(player), + "Duplicator fixer metadata should be applied when legacy vanilla entity stats is disabled"); + } + + @Test + void legacyVanillaEntityStatsSkipsDuplicatorFixer() { + EngineCfg.LEGACY_VANILLA_ENTITY_STATS = true; + + server.getPluginManager().callEvent(new PlayerToggleSprintEvent(player, true)); + + assertFalse(EntityManager.isPacketDuplicatorFixed(player), + "Duplicator fixer metadata should not be applied when legacy vanilla entity stats is enabled"); + } +} diff --git a/src/test/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListenerTest.java b/src/test/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListenerTest.java index 19102fc0..13c71f66 100644 --- a/src/test/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListenerTest.java +++ b/src/test/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListenerTest.java @@ -3,6 +3,10 @@ import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.entity.Trident; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.entity.EntityDamageEvent.DamageModifier; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.persistence.PersistentDataType; @@ -17,9 +21,14 @@ import studio.magemonkey.divinity.api.event.DivinityDamageEvent; import studio.magemonkey.divinity.testutil.MockedTest; +import java.util.EnumMap; +import java.util.Map; +import java.util.function.Function; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +@SuppressWarnings("deprecation") public class VanillaWrapperListenerTest extends MockedTest { private PlayerMock damager; private PlayerMock target; @@ -103,6 +112,101 @@ void tridentUsesSavedCustomWeaponDamageAfterSwap() { }); } + @Test + void resistancePreservedInsteadOfBeingZeroedOut() { + final double base = 10D; + final double resistance = -2D; + + EntityDamageByEntityEvent event = buildDamageEvent(base, resistance, null); + + assertEquals(resistance, + event.getDamage(DamageModifier.RESISTANCE), + 0.001, + "Resistance modifier should be preserved rather than zeroed out"); + assertEquals(base + resistance, + event.getFinalDamage(), + 0.001, + "Final damage should reflect the resistance reduction"); + } + + @Test + void absorptionCappedToVictimsAvailableAmountWhenHitIsLarger() { + target.setAbsorptionAmount(4D); // 2 golden hearts + final double base = 10D; + + EntityDamageByEntityEvent event = buildDamageEvent(base, null, -10D); + + assertEquals(base, + event.getDamage(DamageModifier.BASE), + 0.001, + "Base damage should be left untouched; absorption should not also be subtracted from it"); + assertEquals(-4D, + event.getDamage(DamageModifier.ABSORPTION), + 0.001, + "Absorption modifier should be capped to the victim's available absorption amount"); + assertEquals(6D, event.getFinalDamage(), 0.001, "Absorption should only reduce final damage once"); + } + + @Test + void absorptionNotConsumedBeyondTheIncomingDamage() { + target.setAbsorptionAmount(20D); // 10 golden hearts + final double base = 2D; + + EntityDamageByEntityEvent event = buildDamageEvent(base, null, -20D); + + assertEquals(-2D, + event.getDamage(DamageModifier.ABSORPTION), + 0.001, + "A small hit should only consume absorption equal to its own damage, not drain the whole pool"); + assertEquals(0D, event.getFinalDamage(), 0.001); + } + + @Test + void resistanceIsFactoredInBeforeAbsorptionIsCalculated() { + target.setAbsorptionAmount(4D); // 2 golden hearts + final double base = 10D; + final double resistance = -4D; + + EntityDamageByEntityEvent event = buildDamageEvent(base, resistance, -10D); + + assertEquals(resistance, event.getDamage(DamageModifier.RESISTANCE), 0.001); + assertEquals(-4D, + event.getDamage(DamageModifier.ABSORPTION), + 0.001, + "Absorption should be computed off the damage remaining after resistance, then capped"); + assertEquals(base + resistance - 4D, event.getFinalDamage(), 0.001); + } + + /** + * Fires an {@link EntityDamageByEntityEvent} carrying only the given modifiers (mirroring how the + * vanilla server would populate them before Divinity's listener runs), so the resistance/absorption + * fix in {@code VanillaWrapperListener#onVanillaDamage} can be exercised in isolation. + */ + private EntityDamageByEntityEvent buildDamageEvent(double base, Double resistance, Double absorption) { + Map modifiers = new EnumMap<>(DamageModifier.class); + Map> functions = new EnumMap<>(DamageModifier.class); + + modifiers.put(DamageModifier.BASE, base); + functions.put(DamageModifier.BASE, d -> base); + + if (resistance != null) { + modifiers.put(DamageModifier.RESISTANCE, resistance); + functions.put(DamageModifier.RESISTANCE, d -> resistance); + } + if (absorption != null) { + modifiers.put(DamageModifier.ABSORPTION, absorption); + functions.put(DamageModifier.ABSORPTION, d -> absorption); + } + + EntityDamageByEntityEvent event = new EntityDamageByEntityEvent(damager, + target, + DamageCause.ENTITY_ATTACK, + modifiers, + functions); + server.getPluginManager().callEvent(event); + return event; + } + private double getTotalDamage(Start event) { return event.getDamageMap().values().stream().mapToDouble(Double::doubleValue).sum(); } diff --git a/src/test/java/studio/magemonkey/divinity/stats/items/ItemStatsTest.java b/src/test/java/studio/magemonkey/divinity/stats/items/ItemStatsTest.java new file mode 100644 index 00000000..a3407782 --- /dev/null +++ b/src/test/java/studio/magemonkey/divinity/stats/items/ItemStatsTest.java @@ -0,0 +1,40 @@ +package studio.magemonkey.divinity.stats.items; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockbukkit.mockbukkit.entity.PlayerMock; +import studio.magemonkey.divinity.config.EngineCfg; +import studio.magemonkey.divinity.testutil.MockedTest; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ItemStatsTest extends MockedTest { + private PlayerMock player; + + @BeforeEach + void setup() { + player = genPlayer("Travja"); + } + + @AfterEach + void resetFlag() { + EngineCfg.LEGACY_VANILLA_ITEM_STATS = false; + } + + @Test + void legacyVanillaItemStatsLeavesItemCompletelyUntouched() { + EngineCfg.LEGACY_VANILLA_ITEM_STATS = true; + + ItemStack item = new ItemStack(Material.DIAMOND_SWORD); + ItemStack before = item.clone(); + + ItemStats.updateVanillaAttributes(item, player); + + assertEquals(before, + item, + "Item should not be modified at all while legacy vanilla item stats is enabled"); + } +} From b95b2ee57c4e85f335b3d4d4dbc8e16771023a20 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 25 Jul 2026 01:26:08 +0000 Subject: [PATCH 07/20] Fix compile error: use Guava's Function for damage event modifiers This Bukkit version's EntityDamageByEntityEvent constructor expects com.google.common.base.Function rather than java.util.function.Function. --- .../manager/listener/object/VanillaWrapperListenerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListenerTest.java b/src/test/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListenerTest.java index 13c71f66..802c7c26 100644 --- a/src/test/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListenerTest.java +++ b/src/test/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListenerTest.java @@ -21,9 +21,10 @@ import studio.magemonkey.divinity.api.event.DivinityDamageEvent; import studio.magemonkey.divinity.testutil.MockedTest; +import com.google.common.base.Function; + import java.util.EnumMap; import java.util.Map; -import java.util.function.Function; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; From 7b118f452ce8becb6ad131369a4596ed3ef12677 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 08:44:41 +0200 Subject: [PATCH 08/20] Update src/main/java/studio/magemonkey/divinity/config/EngineCfg.java Co-authored-by: Trav --- src/main/java/studio/magemonkey/divinity/config/EngineCfg.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java index 3f71b5d7..00808065 100644 --- a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java +++ b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java @@ -114,7 +114,7 @@ public EngineCfg(@NotNull Divinity plugin) throws InvalidConfigurationException public static String LORE_STYLE_FABLED_ATTRIBUTE_FORMAT; - public static boolean LEGACY_VANILLA_ENTITY_STATS; + public static boolean VANILLA_ONLY_ENTITY_STATS; public static boolean LEGACY_VANILLA_ITEM_STATS; public void setup() { From 6011695812747735ffb3702a7fd9c13af0eba63c Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 08:44:53 +0200 Subject: [PATCH 09/20] Update src/main/java/studio/magemonkey/divinity/config/EngineCfg.java Co-authored-by: Trav --- src/main/java/studio/magemonkey/divinity/config/EngineCfg.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java index 00808065..3552f89c 100644 --- a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java +++ b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java @@ -115,7 +115,7 @@ public EngineCfg(@NotNull Divinity plugin) throws InvalidConfigurationException public static String LORE_STYLE_FABLED_ATTRIBUTE_FORMAT; public static boolean VANILLA_ONLY_ENTITY_STATS; - public static boolean LEGACY_VANILLA_ITEM_STATS; + public static boolean VANILLA_ONLY_ITEM_STATS; public void setup() { this.plugin.info("Loading engine configuration..."); From 6b8ca18c32db9e3d512ebafeb118f4c39e204225 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 08:45:02 +0200 Subject: [PATCH 10/20] Update src/main/java/studio/magemonkey/divinity/config/EngineCfg.java Co-authored-by: Trav --- src/main/java/studio/magemonkey/divinity/config/EngineCfg.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java index 3552f89c..d0c0a493 100644 --- a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java +++ b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java @@ -432,7 +432,7 @@ public void setup() { path = "legacy.vanilla-entity-stats"; cfg.addMissing(path, false); - EngineCfg.LEGACY_VANILLA_ENTITY_STATS = cfg.getBoolean(path, false); + EngineCfg.VANILLA_ONLY_ENTITY_STATS = cfg.getBoolean(path, false); path = "legacy.vanilla-item-stats"; cfg.addMissing(path, false); EngineCfg.LEGACY_VANILLA_ITEM_STATS = cfg.getBoolean(path, false); From 82cd9f0e75cf9d1f2243c1c104e459a4cf2448b7 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 08:45:09 +0200 Subject: [PATCH 11/20] Update src/main/java/studio/magemonkey/divinity/config/EngineCfg.java Co-authored-by: Trav --- src/main/java/studio/magemonkey/divinity/config/EngineCfg.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java index d0c0a493..1900940a 100644 --- a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java +++ b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java @@ -435,7 +435,7 @@ public void setup() { EngineCfg.VANILLA_ONLY_ENTITY_STATS = cfg.getBoolean(path, false); path = "legacy.vanilla-item-stats"; cfg.addMissing(path, false); - EngineCfg.LEGACY_VANILLA_ITEM_STATS = cfg.getBoolean(path, false); + EngineCfg.VANILLA_ONLY_ITEM_STATS = cfg.getBoolean(path, false); cfg.saveChanges(); } From ebcd718a3db092b07ca47cc2722a4ea9732f5e65 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 08:45:15 +0200 Subject: [PATCH 12/20] Update src/main/java/studio/magemonkey/divinity/manager/EntityManager.java Co-authored-by: Trav --- .../java/studio/magemonkey/divinity/manager/EntityManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java b/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java index 125ae97b..66022df8 100644 --- a/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java +++ b/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java @@ -92,7 +92,7 @@ public void shutdown() { @EventHandler(priority = EventPriority.MONITOR) public void onStatsDeath(EntityDeathEvent e) { - if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; + if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return; LivingEntity entity = e.getEntity(); previousEquipment.remove(e.getEntity().getUniqueId()); EntityStats.get(entity).handleDeath(); From 0059cc32a612e3aca90694163e68fabbc830f029 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 08:45:22 +0200 Subject: [PATCH 13/20] Update src/main/java/studio/magemonkey/divinity/stats/EntityStats.java Co-authored-by: Trav --- src/main/java/studio/magemonkey/divinity/stats/EntityStats.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/studio/magemonkey/divinity/stats/EntityStats.java b/src/main/java/studio/magemonkey/divinity/stats/EntityStats.java index f7b29b2e..8ace575a 100644 --- a/src/main/java/studio/magemonkey/divinity/stats/EntityStats.java +++ b/src/main/java/studio/magemonkey/divinity/stats/EntityStats.java @@ -438,7 +438,7 @@ private void updateInventory() { } public void updateAll() { - if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; + if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return; if (!EngineCfg.ATTRIBUTES_EFFECTIVE_FOR_MOBS && !this.isPlayer()) { return; } From cc0a05b15aeaad1e313674fb02d9798a6f90bca9 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 08:45:27 +0200 Subject: [PATCH 14/20] Update src/main/java/studio/magemonkey/divinity/manager/listener/object/DynamicStatListener.java Co-authored-by: Trav --- .../divinity/manager/listener/object/DynamicStatListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/studio/magemonkey/divinity/manager/listener/object/DynamicStatListener.java b/src/main/java/studio/magemonkey/divinity/manager/listener/object/DynamicStatListener.java index 5b1d082e..4e455347 100644 --- a/src/main/java/studio/magemonkey/divinity/manager/listener/object/DynamicStatListener.java +++ b/src/main/java/studio/magemonkey/divinity/manager/listener/object/DynamicStatListener.java @@ -28,7 +28,7 @@ public DynamicStatListener(@NotNull Divinity plugin) { } public static void updateItem(@Nullable Player p, @NotNull ItemStack item) { - if(EngineCfg.LEGACY_VANILLA_ITEM_STATS) return; + if(EngineCfg.VANILLA_ONLY_ITEM_STATS) return; for (DynamicStat dynamicStat : ItemStats.getDynamicStats()) { dynamicStat.updateItem(p, item); } From 63bc1fe59aa71d1dca6bd6cbf7ee9915fcc425b7 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 08:45:33 +0200 Subject: [PATCH 15/20] Update src/main/java/studio/magemonkey/divinity/stats/items/ItemStats.java Co-authored-by: Trav --- .../java/studio/magemonkey/divinity/stats/items/ItemStats.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/studio/magemonkey/divinity/stats/items/ItemStats.java b/src/main/java/studio/magemonkey/divinity/stats/items/ItemStats.java index 51e15b6a..5bcdaa80 100644 --- a/src/main/java/studio/magemonkey/divinity/stats/items/ItemStats.java +++ b/src/main/java/studio/magemonkey/divinity/stats/items/ItemStats.java @@ -309,7 +309,7 @@ public static boolean hasStat(@NotNull ItemStack item, @Nullable Player player, // ----------------------------------------------------------------- // public static void updateVanillaAttributes(@NotNull ItemStack item, @Nullable Player player) { - if (EngineCfg.LEGACY_VANILLA_ITEM_STATS) return; + if (EngineCfg.VANILLA_ONLY_ITEM_STATS) return; addAttribute(item, player, NBTAttribute.MAX_HEALTH, getStat(item, player, TypedStat.Type.MAX_HEALTH)); addAttribute(item, player, NBTAttribute.MOVEMENT_SPEED, getStat(item, player, TypedStat.Type.MOVEMENT_SPEED)); From 66ffdb3006ee728270d6aa82835e13c8a2ac0c3d Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 08:45:54 +0200 Subject: [PATCH 16/20] Update src/main/java/studio/magemonkey/divinity/config/EngineCfg.java Co-authored-by: Trav --- src/main/java/studio/magemonkey/divinity/config/EngineCfg.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java index 1900940a..7a41bea5 100644 --- a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java +++ b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java @@ -433,7 +433,7 @@ public void setup() { path = "legacy.vanilla-entity-stats"; cfg.addMissing(path, false); EngineCfg.VANILLA_ONLY_ENTITY_STATS = cfg.getBoolean(path, false); - path = "legacy.vanilla-item-stats"; + path = "vanilla-only.item-stats"; cfg.addMissing(path, false); EngineCfg.VANILLA_ONLY_ITEM_STATS = cfg.getBoolean(path, false); From 8ab2fdb7c1293862272ad973777d9ffcd081a687 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 08:46:32 +0200 Subject: [PATCH 17/20] Update src/main/java/studio/magemonkey/divinity/config/EngineCfg.java Co-authored-by: Trav --- src/main/java/studio/magemonkey/divinity/config/EngineCfg.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java index 7a41bea5..89fbf0bc 100644 --- a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java +++ b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java @@ -430,7 +430,7 @@ public void setup() { StringUT.color(cfg.getString(path, "&7%attrPre%&3%name%&7%attrPost%")); - path = "legacy.vanilla-entity-stats"; + path = "vanilla-only.entity-stats"; cfg.addMissing(path, false); EngineCfg.VANILLA_ONLY_ENTITY_STATS = cfg.getBoolean(path, false); path = "vanilla-only.item-stats"; From dca9fc5de1f6ea944a2a26bc14a7760a9c5dadba Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 08:59:16 +0200 Subject: [PATCH 18/20] Further constant renamings --- .../divinity/manager/EntityManager.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java b/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java index 66022df8..af4f2bcc 100644 --- a/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java +++ b/src/main/java/studio/magemonkey/divinity/manager/EntityManager.java @@ -101,26 +101,26 @@ public void onStatsDeath(EntityDeathEvent e) { // Clear stats on player exit @EventHandler(priority = EventPriority.HIGHEST) public void onStatsQuit(PlayerQuitEvent e) { - if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; + if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return; EntityStats.purge(e.getPlayer()); } @EventHandler(priority = EventPriority.HIGHEST) public void onStatsJoin(PlayerJoinEvent e) { - if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; + if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return; EntityStats.get(e.getPlayer()); this.pushToUpdate(e.getPlayer(), 1D); } @EventHandler public void quit(PlayerQuitEvent event) { - if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; + if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return; previousEquipment.remove(event.getPlayer().getUniqueId()); } @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onStatsRegen(EntityRegainHealthEvent e) { - if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; + if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return; Entity e1 = e.getEntity(); if (!(e1 instanceof LivingEntity)) return; @@ -131,7 +131,7 @@ public void onStatsRegen(EntityRegainHealthEvent e) { @EventHandler(ignoreCancelled = true) public void onPickup(EntityPickupItemEvent e) { - if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; + if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return; if (!ProjectileStats.isPickable(e.getItem())) { e.setCancelled(true); } @@ -146,7 +146,7 @@ public void onPickup(EntityPickupItemEvent e) { } private final void pushToUpdate(@NotNull LivingEntity entity, double time) { - if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; + if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return; EntityEquipment equip = new EntityEquipmentSnapshot(entity); previousEquipment.put(entity.getUniqueId(), equip); if (time <= 0D) { @@ -162,7 +162,7 @@ public void run() { } private void updateVanillaItemAttributes(@NotNull LivingEntity entity) { - if (EngineCfg.LEGACY_VANILLA_ITEM_STATS) return; + if (EngineCfg.VANILLA_ONLY_ENTITY_STATS) return; EntityEquipment equipment = entity.getEquipment(); if (equipment == null) return; @@ -179,7 +179,7 @@ private void updateVanillaItemAttributes(@NotNull LivingEntity entity) { } private final void addDuplicatorFixer(@NotNull Entity entity) { - if(EngineCfg.LEGACY_VANILLA_ENTITY_STATS) return; + if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return; entity.setMetadata(PACKET_DUPLICATOR_FIXER, new FixedMetadataValue(plugin, "fixed")); } From 6088c0a30dacdcd12952a3ff5ad04585af9ff7ac Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 09:03:42 +0200 Subject: [PATCH 19/20] resolved building --- .../magemonkey/divinity/manager/EntityManagerTest.java | 6 +++--- .../magemonkey/divinity/stats/items/ItemStatsTest.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/studio/magemonkey/divinity/manager/EntityManagerTest.java b/src/test/java/studio/magemonkey/divinity/manager/EntityManagerTest.java index 07e070bf..0d533496 100644 --- a/src/test/java/studio/magemonkey/divinity/manager/EntityManagerTest.java +++ b/src/test/java/studio/magemonkey/divinity/manager/EntityManagerTest.java @@ -21,12 +21,12 @@ void setup() { @AfterEach void resetFlag() { - EngineCfg.LEGACY_VANILLA_ENTITY_STATS = false; + EngineCfg.VANILLA_ONLY_ENTITY_STATS = false; } @Test void entityStatsHandlingAppliesDuplicatorFixerByDefault() { - EngineCfg.LEGACY_VANILLA_ENTITY_STATS = false; + EngineCfg.VANILLA_ONLY_ENTITY_STATS = false; server.getPluginManager().callEvent(new PlayerToggleSprintEvent(player, true)); @@ -36,7 +36,7 @@ void entityStatsHandlingAppliesDuplicatorFixerByDefault() { @Test void legacyVanillaEntityStatsSkipsDuplicatorFixer() { - EngineCfg.LEGACY_VANILLA_ENTITY_STATS = true; + EngineCfg.VANILLA_ONLY_ENTITY_STATS = true; server.getPluginManager().callEvent(new PlayerToggleSprintEvent(player, true)); diff --git a/src/test/java/studio/magemonkey/divinity/stats/items/ItemStatsTest.java b/src/test/java/studio/magemonkey/divinity/stats/items/ItemStatsTest.java index a3407782..29ca5cc4 100644 --- a/src/test/java/studio/magemonkey/divinity/stats/items/ItemStatsTest.java +++ b/src/test/java/studio/magemonkey/divinity/stats/items/ItemStatsTest.java @@ -21,12 +21,12 @@ void setup() { @AfterEach void resetFlag() { - EngineCfg.LEGACY_VANILLA_ITEM_STATS = false; + EngineCfg.VANILLA_ONLY_ITEM_STATS = false; } @Test void legacyVanillaItemStatsLeavesItemCompletelyUntouched() { - EngineCfg.LEGACY_VANILLA_ITEM_STATS = true; + EngineCfg.VANILLA_ONLY_ITEM_STATS = true; ItemStack item = new ItemStack(Material.DIAMOND_SWORD); ItemStack before = item.clone(); From 4fb4141047e65daaa1d75bd22ec840e61b7c0492 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Sat, 25 Jul 2026 11:12:50 +0200 Subject: [PATCH 20/20] included a vanilla only damage handling configuration --- .../java/studio/magemonkey/divinity/config/EngineCfg.java | 4 ++++ .../manager/listener/object/VanillaWrapperListener.java | 2 ++ src/main/resources/engine.yml | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java index 89fbf0bc..3f6cb524 100644 --- a/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java +++ b/src/main/java/studio/magemonkey/divinity/config/EngineCfg.java @@ -63,6 +63,7 @@ public EngineCfg(@NotNull Divinity plugin) throws InvalidConfigurationException public static boolean COMBAT_BOWS_DO_FULL_MELEE_DAMAGE; public static double COMBAT_DAMAGE_MODIFIER_FOR_COOLDOWN; public static double COMBAT_MAX_GET_TARGET_DISTANCE; + public static boolean VANILLA_ONLY_DAMAGE_HANDLING; public static String LORE_CHAR_PERCENT; public static String LORE_CHAR_NEGATIVE; @@ -436,6 +437,9 @@ public void setup() { path = "vanilla-only.item-stats"; cfg.addMissing(path, false); EngineCfg.VANILLA_ONLY_ITEM_STATS = cfg.getBoolean(path, false); + path = "vanilla-only.damage-handling"; + cfg.addMissing(path, false); + EngineCfg.VANILLA_ONLY_DAMAGE_HANDLING = cfg.getBoolean(path, false); cfg.saveChanges(); } diff --git a/src/main/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListener.java b/src/main/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListener.java index 07e9df8d..a3acfca3 100644 --- a/src/main/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListener.java +++ b/src/main/java/studio/magemonkey/divinity/manager/listener/object/VanillaWrapperListener.java @@ -139,6 +139,8 @@ public void onVanillaProjectLaunch(ProjectileLaunchEvent e) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onVanillaDamage(EntityDamageEvent e) { + if (EngineCfg.VANILLA_ONLY_DAMAGE_HANDLING) return; + boolean isEde = e instanceof EntityDamageByEntityEvent; if (isEde && plugin.getPluginManager().isPluginEnabled("Fabled")) { EntityDamageByEntityEvent ede = (EntityDamageByEntityEvent) e; diff --git a/src/main/resources/engine.yml b/src/main/resources/engine.yml index 8f1ad776..13598b15 100644 --- a/src/main/resources/engine.yml +++ b/src/main/resources/engine.yml @@ -232,3 +232,11 @@ lore: main: '&c▸ %name% %value%' max-roman: 10 fabled-attribute-format: '&7%attrPre%&3%name%&7%attrPost%' + +vanilla-only: + # When enabled, Divinity will not replace vanilla entity-stat handling. + entity-stats: false + # When enabled, Divinity will not add generated vanilla item attributes. + item-stats: false + # When enabled, Divinity leaves damage events to Minecraft's vanilla handling. + damage-handling: false