Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9ae84a1
re-implement hide-flags param for engine.yml (lesser revert of 00d677f)
MaksyKun Jul 12, 2026
93a0cf1
added backward/restore of original flags of an item
MaksyKun Jul 12, 2026
1325fe9
restored legacy vanilla configurations with more seperated configurat…
MaksyKun Jul 24, 2026
6182a3e
fixed resistance and absorption behaviour
MaksyKun Jul 24, 2026
6315aee
preserve vanilla attributes before stats
MaksyKun Jul 24, 2026
aa6641a
Add tests for resistance/absorption fix and legacy vanilla flags
claude Jul 25, 2026
b95b2ee
Fix compile error: use Guava's Function for damage event modifiers
claude Jul 25, 2026
7b118f4
Update src/main/java/studio/magemonkey/divinity/config/EngineCfg.java
MaksyKun Jul 25, 2026
6011695
Update src/main/java/studio/magemonkey/divinity/config/EngineCfg.java
MaksyKun Jul 25, 2026
6b8ca18
Update src/main/java/studio/magemonkey/divinity/config/EngineCfg.java
MaksyKun Jul 25, 2026
82cd9f0
Update src/main/java/studio/magemonkey/divinity/config/EngineCfg.java
MaksyKun Jul 25, 2026
ebcd718
Update src/main/java/studio/magemonkey/divinity/manager/EntityManager…
MaksyKun Jul 25, 2026
0059cc3
Update src/main/java/studio/magemonkey/divinity/stats/EntityStats.java
MaksyKun Jul 25, 2026
cc0a05b
Update src/main/java/studio/magemonkey/divinity/manager/listener/obje…
MaksyKun Jul 25, 2026
63bc1fe
Update src/main/java/studio/magemonkey/divinity/stats/items/ItemStats…
MaksyKun Jul 25, 2026
66ffdb3
Update src/main/java/studio/magemonkey/divinity/config/EngineCfg.java
MaksyKun Jul 25, 2026
8ab2fdb
Update src/main/java/studio/magemonkey/divinity/config/EngineCfg.java
MaksyKun Jul 25, 2026
dca9fc5
Further constant renamings
MaksyKun Jul 25, 2026
f1b26dd
Merge pull request #340 from magemonkeystudio/claude/pr-339-explanati…
MaksyKun Jul 25, 2026
6088c0a
resolved building
MaksyKun Jul 25, 2026
4fb4141
included a vanilla only damage handling configuration
MaksyKun Jul 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/main/java/studio/magemonkey/divinity/config/EngineCfg.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -62,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;
Expand Down Expand Up @@ -113,6 +115,9 @@ 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 VANILLA_ONLY_ITEM_STATS;

public void setup() {
this.plugin.info("Loading engine configuration...");

Expand Down Expand Up @@ -174,10 +179,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");
Expand Down Expand Up @@ -424,6 +430,17 @@ public void setup() {
EngineCfg.LORE_STYLE_FABLED_ATTRIBUTE_FORMAT =
StringUT.color(cfg.getString(path, "&7%attrPre%&3%name%&7%attrPost%"));


path = "vanilla-only.entity-stats";
cfg.addMissing(path, false);
EngineCfg.VANILLA_ONLY_ENTITY_STATS = cfg.getBoolean(path, false);
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();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -19,6 +20,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;
Expand Down Expand Up @@ -90,6 +92,7 @@ public void shutdown() {

@EventHandler(priority = EventPriority.MONITOR)
public void onStatsDeath(EntityDeathEvent e) {
if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return;
LivingEntity entity = e.getEntity();
previousEquipment.remove(e.getEntity().getUniqueId());
EntityStats.get(entity).handleDeath();
Expand All @@ -98,22 +101,26 @@ public void onStatsDeath(EntityDeathEvent e) {
// Clear stats on player exit
@EventHandler(priority = EventPriority.HIGHEST)
public void onStatsQuit(PlayerQuitEvent e) {
if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return;
EntityStats.purge(e.getPlayer());
}

@EventHandler(priority = EventPriority.HIGHEST)
public void onStatsJoin(PlayerJoinEvent e) {
if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return;
EntityStats.get(e.getPlayer());
this.pushToUpdate(e.getPlayer(), 1D);
}

@EventHandler
public void quit(PlayerQuitEvent event) {
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.VANILLA_ONLY_ENTITY_STATS) return;
Entity e1 = e.getEntity();
if (!(e1 instanceof LivingEntity)) return;

Expand All @@ -124,6 +131,7 @@ public void onStatsRegen(EntityRegainHealthEvent e) {

@EventHandler(ignoreCancelled = true)
public void onPickup(EntityPickupItemEvent e) {
if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return;
if (!ProjectileStats.isPickable(e.getItem())) {
e.setCancelled(true);
}
Expand All @@ -138,6 +146,7 @@ public void onPickup(EntityPickupItemEvent e) {
}

private final void pushToUpdate(@NotNull LivingEntity entity, double time) {
if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return;
EntityEquipment equip = new EntityEquipmentSnapshot(entity);
previousEquipment.put(entity.getUniqueId(), equip);
if (time <= 0D) {
Expand All @@ -152,7 +161,25 @@ public void run() {
}.runTask(Divinity.getInstance());
}

private void updateVanillaItemAttributes(@NotNull LivingEntity entity) {
if (EngineCfg.VANILLA_ONLY_ENTITY_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.VANILLA_ONLY_ENTITY_STATS) return;
entity.setMetadata(PACKET_DUPLICATOR_FIXER, new FixedMetadataValue(plugin, "fixed"));
}

Expand Down Expand Up @@ -197,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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -27,6 +28,7 @@ public DynamicStatListener(@NotNull Divinity plugin) {
}

public static void updateItem(@Nullable Player p, @NotNull ItemStack item) {
if(EngineCfg.VANILLA_ONLY_ITEM_STATS) return;
for (DynamicStat<?> dynamicStat : ItemStats.getDynamicStats()) {
dynamicStat.updateItem(p, item);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +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<Divinity> {


Expand Down Expand Up @@ -106,8 +110,20 @@ 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());
} 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -376,16 +378,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
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ private void updateInventory() {
}

public void updateAll() {
if(EngineCfg.VANILLA_ONLY_ENTITY_STATS) return;
if (!EngineCfg.ATTRIBUTES_EFFECTIVE_FOR_MOBS && !this.isPlayer()) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.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));
addAttribute(item, player, NBTAttribute.ATTACK_SPEED, getStat(item, player, TypedStat.Type.ATTACK_SPEED));
Expand Down
10 changes: 10 additions & 0 deletions src/main/resources/engine.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -230,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
Original file line number Diff line number Diff line change
@@ -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.VANILLA_ONLY_ENTITY_STATS = false;
}

@Test
void entityStatsHandlingAppliesDuplicatorFixerByDefault() {
EngineCfg.VANILLA_ONLY_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.VANILLA_ONLY_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");
}
}
Loading
Loading