Skip to content
Open
Changes from 1 commit
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,240 @@
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..e21968703dfee71946f64b1262cb35d9b3904b6c 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.entity.EquipmentInfo, 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,11 @@ 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);
+ this.onEquipmentReplaced(oldStack, stack);
+ return oldStack;
+ // Leaves end - Lithium - equipment tracking
}

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

public void setAll(EntityEquipment equipment) {
+ this.onClear(); // Leaves - Lithium - equipment tracking
this.items.clear();
this.items.putAll(equipment.items);
+ // Leaves start - Lithium - equipment tracking
+ for (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 +92,7 @@ public class EntityEquipment {

public void clear() {
this.items.replaceAll((equipmentSlot, itemStack) -> ItemStack.EMPTY);
+ this.onClear(); // Leaves - Lithium - equipment tracking
}

// Paper start - EntityDeathEvent
@@ -78,4 +101,99 @@ public class EntityEquipment {
return this.items.containsKey(slot);
}
// Paper end - EntityDeathEvent
+
+ @Override
+ public boolean lithium$shouldTickEnchantments() {
+ this.processScheduledEnchantmentCheck(null);
+ return this.shouldTickEnchantments;
+ }
+
+ @Override
+ public boolean lithium$hasUnsentEquipmentChanges() {
+ return this.hasUnsentEquipmentChanges;
+ }
+
+ @Override
+ 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();
+ }
}
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 951300caeca0421cabda44496ed2f09fc2258dd0..94d3ad59dbf105e827c69bac2fce67edbe3ea4ba 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -425,7 +425,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
this.getSleepingPos().ifPresent(this::setPosToBed);
}

- if (this.level() instanceof ServerLevel serverLevel) {
+ if ((this instanceof Player || this.equipment.lithium$shouldTickEnchantments()) && this.level() instanceof ServerLevel serverLevel) { // Leaves - Lithium - equipment tracking
EnchantmentHelper.tickEffects(serverLevel, this);
}

@@ -3348,6 +3348,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
public void detectEquipmentUpdates() {
Map<EquipmentSlot, ItemStack> map = this.collectEquipmentChanges();
if (map != null) {
+ if (!(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 +3358,10 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin

@Nullable
private Map<EquipmentSlot, ItemStack> collectEquipmentChanges() {
+ // Leaves start - Lithium - equipment tracking
+ 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..e20c90c6db23ce82f19526cfd384d52d4af06afc 100644
--- a/net/minecraft/world/entity/decoration/ArmorStand.java
+++ b/net/minecraft/world/entity/decoration/ArmorStand.java
@@ -530,8 +530,9 @@ public class ArmorStand extends LivingEntity {
public void tick() {
if (!this.canTick) {
if (this.noTickEquipmentDirty) {
- this.noTickEquipmentDirty = false;
+ //this.noTickEquipmentDirty = false; // Leaves - Lithium - equipment tracking - move down
this.detectEquipmentUpdates();
+ this.noTickEquipmentDirty = false; // Leaves - Lithium - equipment tracking
}

return;
Loading