From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> Date: Wed, 1 Oct 2025 01:49:00 +0300 Subject: [PATCH] lithium: equipment_tracking This patch is based on the following mixins: * "net/caffeinemc/mods/lithium/mixin/util/item_component_and_count_tracking/PatchedDataComponentMapMixin.java" * "net/caffeinemc/mods/lithium/mixin/util/item_component_and_count_tracking/ItemStackMixin.java" * "net/caffeinemc/mods/lithium/mixin/entity/equipment_tracking/enchantment_ticking/LivingEntityMixin.java" * "net/caffeinemc/mods/lithium/mixin/entity/equipment_tracking/equipment_changes/LivingEntityMixin.java" * "net/caffeinemc/mods/lithium/mixin/entity/equipment_tracking/EntityEquipmentMixin.java" * "net/caffeinemc/mods/lithium/common/util/change_tracking/ChangePublisher.java" * "net/caffeinemc/mods/lithium/common/util/change_tracking/ChangeSubscriber.java" By: 2No2Name <2No2Name@web.de> As part of: Lithium (https://github.com/CaffeineMC/lithium) Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) diff --git a/net/minecraft/world/entity/EntityEquipment.java b/net/minecraft/world/entity/EntityEquipment.java index 1e00a7bd89d885cabb4b9ca3c86fbd8cd93cebf5..4fb31f578c2a4a5dc137c38de8f641597ce80465 100644 --- a/net/minecraft/world/entity/EntityEquipment.java +++ b/net/minecraft/world/entity/EntityEquipment.java @@ -7,7 +7,7 @@ import java.util.Objects; import java.util.Map.Entry; import net.minecraft.world.item.ItemStack; -public class EntityEquipment { +public class EntityEquipment implements net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.CountChangeSubscriber { // DivineMC - lithium: equipment_tracking public static final Codec CODEC = Codec.unboundedMap(EquipmentSlot.CODEC, ItemStack.CODEC).xmap(map -> { EnumMap map1 = new EnumMap<>(EquipmentSlot.class); map1.putAll((Map)map); @@ -18,6 +18,11 @@ public class EntityEquipment { return map; }); private final EnumMap items; + // DivineMC start - lithium: equipment_tracking + boolean shouldTickEnchantments = false; + ItemStack recheckEnchantmentForStack = null; + boolean hasUnsentEquipmentChanges = true; + // DivineMC end - lithium: equipment_tracking private EntityEquipment(EnumMap items) { this.items = items; @@ -28,7 +33,13 @@ public class EntityEquipment { } public ItemStack set(EquipmentSlot slot, ItemStack stack) { - return Objects.requireNonNullElse(this.items.put(slot, stack), ItemStack.EMPTY); + // DivineMC start - lithium: equipment_tracking + ItemStack oldStack = Objects.requireNonNullElse(this.items.put(slot, stack), ItemStack.EMPTY); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.equipmentTracking) { + this.onEquipmentReplaced(oldStack, stack); + } + return oldStack; + // DivineMC end - lithium: equipment_tracking } public ItemStack get(EquipmentSlot slot) { @@ -55,8 +66,23 @@ public class EntityEquipment { } public void setAll(EntityEquipment equipment) { + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.equipmentTracking) this.onClear(); // DivineMC - lithium: equipment_tracking this.items.clear(); this.items.putAll(equipment.items); + // DivineMC start - lithium: equipment_tracking + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.equipmentTracking) { + for (net.minecraft.world.item.ItemStack newStack : this.items.values()) { + if (!newStack.isEmpty()) { + if (!this.shouldTickEnchantments) { + this.shouldTickEnchantments = stackHasTickableEnchantment(newStack); + } + if (!newStack.isEmpty()) { + newStack.lithium$subscribe(this, 0); + } + } + } + } + // DivineMC end - lithium: equipment_tracking } public void dropAll(LivingEntity entity) { @@ -69,6 +95,7 @@ public class EntityEquipment { public void clear() { this.items.replaceAll((equipmentSlot, itemStack) -> ItemStack.EMPTY); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.equipmentTracking) this.onClear(); // DivineMC - lithium: equipment_tracking } // Paper start - EntityDeathEvent @@ -77,4 +104,98 @@ public class EntityEquipment { return this.items.containsKey(slot); } // Paper end - EntityDeathEvent + + // DivineMC start - lithium: equipment_tracking + public boolean lithium$shouldTickEnchantments() { + this.processScheduledEnchantmentCheck(null); + return this.shouldTickEnchantments; + } + + public boolean lithium$hasUnsentEquipmentChanges() { + return this.hasUnsentEquipmentChanges; + } + + public void lithium$onEquipmentChangesSent() { + this.hasUnsentEquipmentChanges = false; + } + + private void onClear() { + this.shouldTickEnchantments = false; + this.recheckEnchantmentForStack = null; + this.hasUnsentEquipmentChanges = true; + + for (ItemStack oldStack : this.items.values()) { + if (!oldStack.isEmpty()) { + oldStack.lithium$unsubscribeWithData(this, 0); + } + } + } + + private void onEquipmentReplaced(ItemStack oldStack, ItemStack newStack) { + if (!this.shouldTickEnchantments) { + if (this.recheckEnchantmentForStack == oldStack) { + this.recheckEnchantmentForStack = null; + } + this.shouldTickEnchantments = stackHasTickableEnchantment(newStack); + } + + this.hasUnsentEquipmentChanges = true; + + if (!oldStack.isEmpty()) { + oldStack.lithium$unsubscribeWithData(this, 0); + } + if (!newStack.isEmpty()) { + newStack.lithium$subscribe(this, 0); + } + } + + private static boolean stackHasTickableEnchantment(ItemStack stack) { + if (!stack.isEmpty()) { + net.minecraft.world.item.enchantment.ItemEnchantments enchantments = stack.get(net.minecraft.core.component.DataComponents.ENCHANTMENTS); + if (enchantments != null && !enchantments.isEmpty()) { + for (net.minecraft.core.Holder enchantmentEntry : enchantments.keySet()) { + if (!enchantmentEntry.value().getEffects(net.minecraft.world.item.enchantment.EnchantmentEffectComponents.TICK).isEmpty()) { + return true; + } + } + } + } + return false; + } + + @Override + public void lithium$notify(@org.jetbrains.annotations.Nullable ItemStack publisher, int zero) { + this.hasUnsentEquipmentChanges = true; + + if (!this.shouldTickEnchantments) { + this.processScheduledEnchantmentCheck(publisher); + this.scheduleEnchantmentCheck(publisher); + } + } + + private void scheduleEnchantmentCheck(@org.jetbrains.annotations.Nullable ItemStack toCheck) { + this.recheckEnchantmentForStack = toCheck; + } + + private void processScheduledEnchantmentCheck(@org.jetbrains.annotations.Nullable ItemStack ignoredStack) { + if (this.recheckEnchantmentForStack != null && this.recheckEnchantmentForStack != ignoredStack) { + this.shouldTickEnchantments = stackHasTickableEnchantment(this.recheckEnchantmentForStack); + this.recheckEnchantmentForStack = null; + } + } + + @Override + public void lithium$notifyCount(ItemStack publisher, int zero, int newCount) { + if (newCount == 0) { + publisher.lithium$unsubscribeWithData(this, zero); + } + + this.onEquipmentReplaced(publisher, ItemStack.EMPTY); + } + + @Override + public void lithium$forceUnsubscribe(ItemStack publisher, int zero) { + throw new UnsupportedOperationException(); + } + // DivineMC end - lithium: equipment_tracking } diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java index 86370c9f6e83e5815922080c10336d394075b4e9..85e287bc66c4e2be6f703c3206fe53bba3d15a6d 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java @@ -439,9 +439,17 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin this.getSleepingPos().ifPresent(this::setPosToBed); } - if (this.level() instanceof ServerLevel serverLevel) { - EnchantmentHelper.tickEffects(serverLevel, this); + // DivineMC start - lithium: equipment_tracking + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.equipmentTracking) { + if ((this instanceof Player || this.equipment.lithium$shouldTickEnchantments()) && this.level() instanceof ServerLevel serverLevel) { + EnchantmentHelper.tickEffects(serverLevel, this); + } + } else { + if (this.level() instanceof ServerLevel serverLevel) { + EnchantmentHelper.tickEffects(serverLevel, this); + } } + // DivineMC end - lithium: equipment_tracking super.baseTick(); if (this.isAlive() && this.level() instanceof ServerLevel serverLevel1) { @@ -3444,6 +3452,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin public void detectEquipmentUpdates() { Map map = this.collectEquipmentChanges(); if (map != null) { + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.equipmentTracking && !(this instanceof net.minecraft.world.entity.player.Player)) this.equipment.lithium$onEquipmentChangesSent(); // DivineMC - lithium: equipment_tracking this.handleHandSwap(map); if (!map.isEmpty()) { this.handleEquipmentChanges(map); @@ -3453,6 +3462,14 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin @Nullable private Map collectEquipmentChanges() { + // DivineMC start - lithium: equipment_tracking + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.equipmentTracking) { + final boolean isArmorStandUpdateNoTick = this instanceof net.minecraft.world.entity.decoration.ArmorStand stand && !stand.canTick && stand.noTickEquipmentDirty; + if (!isArmorStandUpdateNoTick && !this.equipment.lithium$hasUnsentEquipmentChanges()) { + return null; + } + } + // DivineMC end - lithium: equipment_tracking Map map = null; // Paper start - EntityEquipmentChangedEvent record EquipmentChangeImpl(org.bukkit.inventory.ItemStack oldItem, org.bukkit.inventory.ItemStack newItem) implements io.papermc.paper.event.entity.EntityEquipmentChangedEvent.EquipmentChange { diff --git a/net/minecraft/world/entity/decoration/ArmorStand.java b/net/minecraft/world/entity/decoration/ArmorStand.java index 79c652fbbc3ecef289e6358c325d98e509f5c216..434f5a331e0beb582dd8229e697ed1c1eecba8cc 100644 --- a/net/minecraft/world/entity/decoration/ArmorStand.java +++ b/net/minecraft/world/entity/decoration/ArmorStand.java @@ -528,8 +528,9 @@ public class ArmorStand extends LivingEntity { maxUpStep = level().purpurConfig.armorstandStepHeight; // Purpur - Add option to set armorstand step height if (!this.canTick) { if (this.noTickEquipmentDirty) { - this.noTickEquipmentDirty = false; + if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.equipmentTracking) this.noTickEquipmentDirty = false; // DivineMC - lithium: equipment_tracking this.detectEquipmentUpdates(); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.equipmentTracking) this.noTickEquipmentDirty = false; // DivineMC - lithium: equipment_tracking } return;