Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MC_XiaoHei <xor7xiaohei@gmail.com>
Date: Tue, 9 Nov 2077 00:00:00 +0800
Subject: [PATCH] Lithium Equipment Tracking

Origin patch author: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com>
Origin patch link: https://github.com/Winds-Studio/Leaf/blob/ver/1.21.8/leaf-server/minecraft-patches/features/0268-Lithium-equipment-tracking.patch

Should have special treatment to ArmorStand, since Paper introduced the configurable
ArmorStand no-tick, and still gives it ability to update equipment changes.
Thus added a bypass condition in LivingEntity#collectEquipmentChanges, always send
ArmorStand equipment changes even if the ArmorStand is no-tick

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 90814ad07a2686c5a274860395f5aca29cc3bf13..758a8bd797f06cd6998f71b095c475e09906e343 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 org.leavesmc.leaves.lithium.common.util.change_tracking.ChangeSubscriber.CountChangeSubscriber<ItemStack> { // Leaves - Lithium - equipment tracking
public static final Codec<EntityEquipment> CODEC = Codec.unboundedMap(EquipmentSlot.CODEC, ItemStack.CODEC).xmap(map -> {
EnumMap<EquipmentSlot, ItemStack> map1 = new EnumMap<>(EquipmentSlot.class);
map1.putAll((Map<? extends EquipmentSlot, ? extends ItemStack>)map);
@@ -18,6 +18,11 @@ public class EntityEquipment {
return map;
});
private final EnumMap<EquipmentSlot, ItemStack> items;
+ // Leaves start - Lithium - equipment tracking
+ boolean shouldTickEnchantments = false;
+ ItemStack recheckEnchantmentForStack = null;
+ boolean hasUnsentEquipmentChanges = true;
+ // Leaves end - Lithium - equipment tracking

private EntityEquipment(EnumMap<EquipmentSlot, ItemStack> items) {
this.items = items;
@@ -29,7 +34,13 @@ public class EntityEquipment {

public ItemStack set(EquipmentSlot slot, ItemStack stack) {
stack.getItem().verifyComponentsAfterLoad(stack);
- return Objects.requireNonNullElse(this.items.put(slot, stack), ItemStack.EMPTY);
+ // Leaves start - Lithium - equipment tracking
+ ItemStack oldStack = Objects.requireNonNullElse(this.items.put(slot, stack), ItemStack.EMPTY);
+ if (org.leavesmc.leaves.LeavesConfig.performance.equipmentTracking) {
+ this.onEquipmentReplaced(oldStack, stack);
+ }
+ return oldStack;
+ // Leaves end - Lithium - equipment tracking
}

public ItemStack get(EquipmentSlot slot) {
@@ -56,8 +67,23 @@ public class EntityEquipment {
}

public void setAll(EntityEquipment equipment) {
+ if (org.leavesmc.leaves.LeavesConfig.performance.equipmentTracking) this.onClear(); // Leaves - Lithium - equipment tracking
this.items.clear();
this.items.putAll(equipment.items);
+ // Leaves start - Lithium - equipment tracking
+ if (org.leavesmc.leaves.LeavesConfig.performance.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);
+ }
+ }
+ }
+ }
+ // Leaves end - Lithium - equipment tracking
}

public void dropAll(LivingEntity entity) {
@@ -70,6 +96,7 @@ public class EntityEquipment {

public void clear() {
this.items.replaceAll((equipmentSlot, itemStack) -> ItemStack.EMPTY);
+ if (org.leavesmc.leaves.LeavesConfig.performance.equipmentTracking) this.onClear(); // Leaves - Lithium - equipment tracking
}

// Paper start - EntityDeathEvent
@@ -78,4 +105,98 @@ public class EntityEquipment {
return this.items.containsKey(slot);
}
// Paper end - EntityDeathEvent
+
+ // Leaves 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<net.minecraft.world.item.enchantment.Enchantment> 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();
+ }
+ // Leaves end - Lithium - equipment tracking
}
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 951300caeca0421cabda44496ed2f09fc2258dd0..eb1a6644670c126c03ef871c8ac4bc2d0e389872 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -425,9 +425,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);
+ // Leaves start - Lithium - equipment tracking
+ if (org.leavesmc.leaves.LeavesConfig.performance.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);
+ }
}
+ // Leaves end - Lithium - equipment tracking

super.baseTick();
ProfilerFiller profilerFiller = Profiler.get();
@@ -3348,6 +3356,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
public void detectEquipmentUpdates() {
Map<EquipmentSlot, ItemStack> map = this.collectEquipmentChanges();
if (map != null) {
+ if (org.leavesmc.leaves.LeavesConfig.performance.equipmentTracking && !(this instanceof net.minecraft.world.entity.player.Player)) this.equipment.lithium$onEquipmentChangesSent(); // Leaves - Lithium - equipment tracking
this.handleHandSwap(map);
if (!map.isEmpty()) {
this.handleEquipmentChanges(map);
@@ -3357,6 +3366,14 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin

@Nullable
private Map<EquipmentSlot, ItemStack> collectEquipmentChanges() {
+ // Leaves start - Lithium - equipment tracking
+ if (org.leavesmc.leaves.LeavesConfig.performance.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;
+ }
+ }
+ // Leaves end - Lithium - equipment tracking
Map<EquipmentSlot, ItemStack> 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 d7725b5ca689e3d5b512baab04e113be77c0b2ee..abe5d681f81f60facc019660a6eae833e5742cef 100644
--- a/net/minecraft/world/entity/decoration/ArmorStand.java
+++ b/net/minecraft/world/entity/decoration/ArmorStand.java
@@ -89,7 +89,7 @@ public class ArmorStand extends LivingEntity {
// Paper start - Allow ArmorStands not to tick
public boolean canTick = true;
public boolean canTickSetByAPI = false;
- private boolean noTickEquipmentDirty = false;
+ public boolean noTickEquipmentDirty = false; // Leaves - private -> public
// Paper end - Allow ArmorStands not to tick

public ArmorStand(EntityType<? extends ArmorStand> entityType, Level level) {
@@ -530,8 +530,9 @@ public class ArmorStand extends LivingEntity {
public void tick() {
if (!this.canTick) {
if (this.noTickEquipmentDirty) {
- this.noTickEquipmentDirty = false;
+ if (!org.leavesmc.leaves.LeavesConfig.performance.equipmentTracking) this.noTickEquipmentDirty = false; // Leaves - Lithium - equipment tracking - move down when enable
this.detectEquipmentUpdates();
+ if (org.leavesmc.leaves.LeavesConfig.performance.equipmentTracking) this.noTickEquipmentDirty = false; // Leaves - Lithium - equipment tracking
}

return;
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,9 @@ public static class PerformanceRemoveConfig {

@GlobalConfig(value = "sleeping-block-entity", lock = true)
public boolean sleepingBlockEntity = false;

@GlobalConfig(value = "equipment-tracking", lock = true)
public boolean equipmentTracking = false;
}

public static ProtocolConfig protocol = new ProtocolConfig();
Expand Down