mirror of
https://github.com/LeavesMC/Leaves.git
synced 2025-12-19 14:59:32 +00:00
Lithium Equipment Tracking (#736)
* feat: merge leaf patch to us * fix: pass build and fix comment * feat: make it configurable
This commit is contained in:
@@ -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;
|
||||||
@@ -812,6 +812,9 @@ public final class LeavesConfig {
|
|||||||
|
|
||||||
@GlobalConfig(value = "sleeping-block-entity", lock = true)
|
@GlobalConfig(value = "sleeping-block-entity", lock = true)
|
||||||
public boolean sleepingBlockEntity = false;
|
public boolean sleepingBlockEntity = false;
|
||||||
|
|
||||||
|
@GlobalConfig(value = "equipment-tracking", lock = true)
|
||||||
|
public boolean equipmentTracking = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolConfig protocol = new ProtocolConfig();
|
public static ProtocolConfig protocol = new ProtocolConfig();
|
||||||
|
|||||||
Reference in New Issue
Block a user