From a33f10a17afcf68995fc27b46cec0709600bcccb Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 31 May 2025 17:20:41 +0900 Subject: [PATCH] fix attribute crash server --- .../features/0085-Multithreaded-Tracker.patch | 245 +++++++++++------- ...ecessary-calculations-if-player-is-n.patch | 6 +- .../0103-Lithium-equipment-tracking.patch | 8 +- .../features/0119-Only-player-pushable.patch | 8 +- ...ptimize-addOrUpdateTransientModifier.patch | 2 +- .../features/0176-Optimize-AttributeMap.patch | 100 ------- ...-Optimize-getScaledTrackingDistance.patch} | 0 ...ptimize-SynchedEntityData-packDirty.patch} | 0 ...patch => 0178-Optimize-isEyeInFluid.patch} | 0 ...tion.patch => 0179-Paw-optimization.patch} | 0 ...patch => 0180-Cache-block-path-type.patch} | 0 ...ch => 0181-optimize-getEntityStatus.patch} | 0 ...on-optimized-PoweredRailBlock-logic.patch} | 0 .../features/0023-Multithreaded-Tracker.patch | 13 + .../util/map/AttributeInstanceArrayMap.java | 60 +++-- .../leaf/util/map/AttributeInstanceSet.java | 113 ++++++++ 16 files changed, 319 insertions(+), 236 deletions(-) delete mode 100644 leaf-server/minecraft-patches/features/0176-Optimize-AttributeMap.patch rename leaf-server/minecraft-patches/features/{0177-Optimize-getScaledTrackingDistance.patch => 0176-Optimize-getScaledTrackingDistance.patch} (100%) rename leaf-server/minecraft-patches/features/{0178-Optimize-SynchedEntityData-packDirty.patch => 0177-Optimize-SynchedEntityData-packDirty.patch} (100%) rename leaf-server/minecraft-patches/features/{0179-Optimize-isEyeInFluid.patch => 0178-Optimize-isEyeInFluid.patch} (100%) rename leaf-server/minecraft-patches/features/{0180-Paw-optimization.patch => 0179-Paw-optimization.patch} (100%) rename leaf-server/minecraft-patches/features/{0181-Cache-block-path-type.patch => 0180-Cache-block-path-type.patch} (100%) rename leaf-server/minecraft-patches/features/{0182-optimize-getEntityStatus.patch => 0181-optimize-getEntityStatus.patch} (100%) rename leaf-server/minecraft-patches/features/{0183-Rail-Optimization-optimized-PoweredRailBlock-logic.patch => 0182-Rail-Optimization-optimized-PoweredRailBlock-logic.patch} (100%) create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceSet.java diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index b08d943c..f18da995 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -58,6 +58,18 @@ index dd2509996bfd08e8c3f9f2be042229eac6d7692d..a35e9fae8f8da0c42f0616c4f78dc396 private static final byte CHUNK_TICKET_STAGE_NONE = 0; private static final byte CHUNK_TICKET_STAGE_LOADING = 1; +diff --git a/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java b/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java +index 9c0c99b936b4a82ebfe924866e53ec71f7bbe9ad..2ccff968cb2065d34fad4d27573f9e3081edb2f2 100644 +--- a/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java ++++ b/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java +@@ -32,6 +32,7 @@ public class ClientboundUpdateAttributesPacket implements Packet updatePacket = savedData.getUpdatePacket(mapId, serverPlayer); -@@ -433,15 +441,30 @@ public class ServerEntity { - } - - if (this.entity instanceof LivingEntity) { -- Set attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync(); -+ // Leaf start - Multithreaded tracker -+ var attributeMap = ((LivingEntity)this.entity).getAttributes(); -+ Set attributesToSync = attributeMap.getAttributesToSync(); - if (!attributesToSync.isEmpty()) { -+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { -+ synchronized (attributeMap) { -+ // CraftBukkit start - Send scaled max health -+ if (this.entity instanceof ServerPlayer serverPlayer) { -+ serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); -+ } -+ // CraftBukkit end -+ this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); -+ -+ } -+ } else { - // CraftBukkit start - Send scaled max health - if (this.entity instanceof ServerPlayer serverPlayer) { - serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); - } - // CraftBukkit end +@@ -443,7 +451,7 @@ public class ServerEntity { this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); -+ } } -+ // Leaf end - Multithreaded tracker - attributesToSync.clear(); +- attributesToSync.clear(); ++ // attributesToSync.clear(); // Leaf - Multithreaded tracker } + } + diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index 275b640f4536366152f59acf071dd4eba15696c8..a669a59a42f814480879a52d2da5e04c636720de 100644 --- a/net/minecraft/server/level/ServerLevel.java @@ -418,39 +407,50 @@ index 327b3bc89920c4ab02c1126dc63bca05ce3abefe..1415043bee5fbbfcf9dab9184a9418d5 if (this.player.isRemoved()) { LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName()); diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java -index d502325d693539842fd6f5485365e0e9b786b7aa..324f8952a921a3897f4ff48145f0f8645c690318 100644 +index d502325d693539842fd6f5485365e0e9b786b7aa..a99527409e9aae6c8b321a5ed100b2645151087e 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java -@@ -1311,13 +1311,26 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1311,13 +1311,13 @@ public abstract class LivingEntity extends Entity implements Attackable { } private void refreshDirtyAttributes() { - Set attributesToUpdate = this.getAttributes().getAttributesToUpdate(); + // Leaf start - Multithreaded tracker -+ var attributeMap = this.getAttributes(); -+ Set attributesToUpdate = attributeMap.getAttributesToUpdate(); -+ -+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { -+ synchronized (attributeMap) { -+ for (AttributeInstance attributeInstance : attributesToUpdate) { -+ this.onAttributeUpdated(attributeInstance.getAttribute()); -+ } ++ int[] attributesToUpdate = this.getAttributes().getAttributesToUpdateIds(); -+ attributesToUpdate.clear(); -+ } -+ } else { - for (AttributeInstance attributeInstance : attributesToUpdate) { - this.onAttributeUpdated(attributeInstance.getAttribute()); +- for (AttributeInstance attributeInstance : attributesToUpdate) { +- this.onAttributeUpdated(attributeInstance.getAttribute()); ++ for (int attribute : attributesToUpdate) { ++ this.onAttributeUpdated(net.minecraft.core.registries.BuiltInRegistries.ATTRIBUTE.get(attribute).orElseThrow()); } - - attributesToUpdate.clear(); -+ } +- +- attributesToUpdate.clear(); + // Leaf end - Multithreaded tracker } protected void onAttributeUpdated(Holder attribute) { +diff --git a/net/minecraft/world/entity/ai/attributes/Attribute.java b/net/minecraft/world/entity/ai/attributes/Attribute.java +index f8419dde44ebc7324e783f8bee42132d5ec973c3..406767c60ec1a324faaf5d3658b161647497f99b 100644 +--- a/net/minecraft/world/entity/ai/attributes/Attribute.java ++++ b/net/minecraft/world/entity/ai/attributes/Attribute.java +@@ -16,10 +16,15 @@ public class Attribute { + private boolean syncable; + private final String descriptionId; + private Attribute.Sentiment sentiment = Attribute.Sentiment.POSITIVE; ++ // Leaf start - Optimize AttributeMap ++ public final int uid; ++ private static final java.util.concurrent.atomic.AtomicInteger SIZE = new java.util.concurrent.atomic.AtomicInteger(); ++ // Leaf end - Optimize AttributeMap + + protected Attribute(String descriptionId, double defaultValue) { + this.defaultValue = defaultValue; + this.descriptionId = descriptionId; ++ this.uid = SIZE.getAndAdd(1); // Leaf - Optimize AttributeMap + } + + public double getDefaultValue() { diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java -index 8013594bb4844e7a8abf28123958e7f632d39341..93b375f39f10568f6b222607890a9ce67db0e9bb 100644 +index 8013594bb4844e7a8abf28123958e7f632d39341..b502c4a0f3695cc5bee8954f937f64584df1584d 100644 --- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java @@ -24,8 +24,24 @@ public class AttributeInstance { @@ -495,21 +495,21 @@ index 8013594bb4844e7a8abf28123958e7f632d39341..93b375f39f10568f6b222607890a9ce6 } public Set getModifiers() { -@@ -174,6 +196,13 @@ public class AttributeInstance { - } +@@ -142,8 +164,12 @@ public class AttributeInstance { - public void replaceFrom(AttributeInstance instance) { -+ // Leaf start - Multithreaded tracker -+ synchronized (instance) { -+ this.replaceFrom0(instance); -+ } -+ } -+ public void replaceFrom0(AttributeInstance instance) { -+ // Leaf end - Multithreaded tracker - this.baseValue = instance.baseValue; - this.modifierById.clear(); - this.modifierById.putAll(instance.modifierById); -@@ -195,9 +224,19 @@ public class AttributeInstance { + public double getValue() { + if (this.dirty) { +- this.cachedValue = this.calculateValue(); ++ // Leaf start - Multithreaded tracker ++ double value = this.calculateValue(); ++ this.cachedValue = value; + this.dirty = false; ++ return value; ++ // Leaf end - Multithreaded tracker + } + + return this.cachedValue; +@@ -195,9 +221,19 @@ public class AttributeInstance { if (!this.permanentModifiers.isEmpty()) { ListTag listTag = new ListTag(); @@ -532,65 +532,112 @@ index 8013594bb4844e7a8abf28123958e7f632d39341..93b375f39f10568f6b222607890a9ce6 compoundTag.put("modifiers", listTag); } diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java -index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..c0a09b615e9b6c4ec72b8b77a78e7da374d4498b 100644 +index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..0bc846721b1af44904a705f5c4aef897a03824e0 100644 --- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java -@@ -21,8 +21,19 @@ public class AttributeMap { +@@ -20,12 +20,14 @@ import org.slf4j.Logger; + public class AttributeMap { private static final Logger LOGGER = LogUtils.getLogger(); // Gale start - Lithium - replace AI attributes with optimized collections - private final Map, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0); +- private final Map, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0); - private final Set attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); - private final Set attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); + // Leaf start - Multithreaded tracker -+ private final Set attributesToSync; -+ private final Set attributesToUpdate; -+ { -+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { -+ attributesToSync = it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0), this); -+ attributesToUpdate = it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0), this); -+ } else { -+ attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); -+ attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); -+ } -+ } ++ private final Map, AttributeInstance> attributes = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(); ++ private final org.dreeam.leaf.util.map.AttributeInstanceSet attributesToSync = new org.dreeam.leaf.util.map.AttributeInstanceSet((org.dreeam.leaf.util.map.AttributeInstanceArrayMap) attributes); ++ private final org.dreeam.leaf.util.map.AttributeInstanceSet attributesToUpdate = new org.dreeam.leaf.util.map.AttributeInstanceSet((org.dreeam.leaf.util.map.AttributeInstanceArrayMap) attributes); + // Leaf end - Multithreaded tracker // Gale end - Lithium - replace AI attributes with optimized collections private final AttributeSupplier supplier; - private final java.util.function.Function, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations -@@ -60,7 +71,13 @@ public class AttributeMap { +- private final java.util.function.Function, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations ++ //private final java.util.function.Function, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations // Leaf - Optimize AttributeMap + private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables + + public AttributeMap(AttributeSupplier supplier) { +@@ -36,31 +38,54 @@ public class AttributeMap { + this.entity = entity; + // Purpur end - Ridables + this.supplier = defaultAttributes; +- this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations ++ //this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations // Leaf - Optimize AttributeMap + } + +- private void onAttributeModified(AttributeInstance instance) { ++ // Leaf start - Multithreaded tracker ++ private synchronized void onAttributeModified(AttributeInstance instance) { + this.attributesToUpdate.add(instance); + if (instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))) { // Purpur - Ridables + this.attributesToSync.add(instance); + } + } + +- public Set getAttributesToSync() { +- return this.attributesToSync; ++ private static final AttributeInstance[] EMPTY_ATTRIBUTE_INSTANCE = new AttributeInstance[0]; ++ public synchronized Set getAttributesToSync() { ++ var clone = it.unimi.dsi.fastutil.objects.ReferenceArraySet.ofUnchecked(attributesToSync.toArray(EMPTY_ATTRIBUTE_INSTANCE)); ++ this.attributesToSync.clear(); ++ return clone; + } + +- public Set getAttributesToUpdate() { +- return this.attributesToUpdate; ++ public synchronized Set getAttributesToUpdate() { ++ var clone = it.unimi.dsi.fastutil.objects.ReferenceArraySet.ofUnchecked(attributesToUpdate.toArray(EMPTY_ATTRIBUTE_INSTANCE)); ++ this.attributesToUpdate.clear(); ++ return clone; + } + ++ public synchronized int[] getAttributesToUpdateIds() { ++ int[] clone = attributesToUpdate.inner.toIntArray(); ++ this.attributesToUpdate.clear(); ++ return clone; ++ } ++ // Leaf end - Multithreaded tracker ++ + public Collection getSyncableAttributes() { + return this.attributes.values().stream().filter(instance -> instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))).collect(Collectors.toList()); // Purpur - Ridables + } @Nullable public AttributeInstance getInstance(Holder attribute) { - return this.attributes.computeIfAbsent(attribute, this.createInstance); // Gale - Airplane - reduce entity allocations - cache lambda, as for some reason java allocates it anyways -+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { -+ synchronized (this) { -+ return this.attributes.computeIfAbsent(attribute, holder -> this.supplier.createInstance(this::onAttributeModified, (Holder) holder)); ++ // Leaf start - Multithreaded tracker ++ AttributeInstance v; ++ if ((v = this.attributes.get(attribute)) == null) { ++ AttributeInstance newValue; ++ if ((newValue = this.supplier.createInstance(this::onAttributeModified, attribute)) != null) { ++ attributes.put(attribute, newValue); ++ return newValue; + } -+ } else { -+ return this.attributes.computeIfAbsent(attribute, holder -> this.supplier.createInstance(this::onAttributeModified, (Holder) holder)); + } ++ return v; ++ // Leaf end - Multithreaded tracker } public boolean hasAttribute(Holder attribute) { -@@ -176,8 +193,17 @@ public class AttributeMap { +diff --git a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java +index 24710041ccbc70e5506d8d89ae34f0141977f209..05de8a77b389691dd6986f36b4cb8cc0935e21e4 100644 +--- a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java ++++ b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java +@@ -11,7 +11,7 @@ public class AttributeSupplier { + private final Map, AttributeInstance> instances; - // Paper - start - living entity allow attribute registration - public void registerAttribute(Holder attributeBase) { -+ // Leaf start - Multithreaded tracker - AttributeInstance attributeModifiable = new AttributeInstance(attributeBase, AttributeInstance::getAttribute); -- attributes.put(attributeBase, attributeModifiable); -+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { -+ synchronized (this) { -+ attributes.put(attributeBase, attributeModifiable); -+ -+ } -+ } else { -+ attributes.put(attributeBase, attributeModifiable); -+ } -+ // Leaf end - Multithreaded tracker + AttributeSupplier(Map, AttributeInstance> instances) { +- this.instances = instances; ++ this.instances = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(instances); // Leaf - Optimize AttributeMap } - // Paper - end - living entity allow attribute registration + public AttributeInstance getAttributeInstance(Holder attribute) { +@@ -41,7 +41,7 @@ public class AttributeSupplier { + } + + @Nullable +- public AttributeInstance createInstance(Consumer onDirty, Holder attribute) { ++ public AttributeInstance createInstance(Consumer onDirty, Holder attribute) { // Leaf - Multithreaded tracker + AttributeInstance attributeInstance = this.instances.get(attribute); + if (attributeInstance == null) { + return null; diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java index c96f458994818392857642282ec3d492124885da..d345afd14ef6fe2f0a584df5dfa080fd7ab3f47e 100644 --- a/net/minecraft/world/entity/item/PrimedTnt.java diff --git a/leaf-server/minecraft-patches/features/0097-Lithium-Skip-unnecessary-calculations-if-player-is-n.patch b/leaf-server/minecraft-patches/features/0097-Lithium-Skip-unnecessary-calculations-if-player-is-n.patch index 23754ae5..855e6d09 100644 --- a/leaf-server/minecraft-patches/features/0097-Lithium-Skip-unnecessary-calculations-if-player-is-n.patch +++ b/leaf-server/minecraft-patches/features/0097-Lithium-Skip-unnecessary-calculations-if-player-is-n.patch @@ -12,10 +12,10 @@ As part of: Lithium (https://github.com/CaffeineMC/lithium-fabric) Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java -index 324f8952a921a3897f4ff48145f0f8645c690318..3e3fdcff3725841c8b04047a91733c8b98187e0b 100644 +index 0ac8b7bd9d899daf61aeb58f80bdcebe87974d51..eb79e6984810410c646d7b1910694d7df75dccba 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java -@@ -2752,6 +2752,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -2739,6 +2739,7 @@ public abstract class LivingEntity extends Entity implements Attackable { } protected void updateSwingTime() { @@ -23,7 +23,7 @@ index 324f8952a921a3897f4ff48145f0f8645c690318..3e3fdcff3725841c8b04047a91733c8b int currentSwingDuration = this.getCurrentSwingDuration(); if (this.swinging) { this.swingTime++; -@@ -3703,6 +3704,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3690,6 +3691,7 @@ public abstract class LivingEntity extends Entity implements Attackable { protected void updateFallFlying() { this.checkSlowFallDistance(); if (!this.level().isClientSide) { diff --git a/leaf-server/minecraft-patches/features/0103-Lithium-equipment-tracking.patch b/leaf-server/minecraft-patches/features/0103-Lithium-equipment-tracking.patch index ba404481..25b5eb45 100644 --- a/leaf-server/minecraft-patches/features/0103-Lithium-equipment-tracking.patch +++ b/leaf-server/minecraft-patches/features/0103-Lithium-equipment-tracking.patch @@ -76,7 +76,7 @@ index a8c6549f772208cd543607224fef2c2389b14f24..709631db548a16a969a373e26ebbcd69 public boolean equals(Object other) { return this == other diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java -index 3e3fdcff3725841c8b04047a91733c8b98187e0b..ffae6778b471628639c41b1d5f3af08b6b72a86c 100644 +index eb79e6984810410c646d7b1910694d7df75dccba..69e91a732a497b2ffa906088a636947ce9a9ec36 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java @@ -159,7 +159,7 @@ import org.bukkit.event.entity.EntityTeleportEvent; @@ -116,7 +116,7 @@ index 3e3fdcff3725841c8b04047a91733c8b98187e0b..ffae6778b471628639c41b1d5f3af08b Equippable equippable = newItem.get(DataComponents.EQUIPPABLE); if (!this.isSilent() && equippable != null && slot == equippable.slot() && !silent) { // CraftBukkit this.level() -@@ -3368,6 +3373,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3355,6 +3360,7 @@ public abstract class LivingEntity extends Entity implements Attackable { public void detectEquipmentUpdatesPublic() { // CraftBukkit Map map = this.collectEquipmentChanges(); if (map != null) { @@ -124,7 +124,7 @@ index 3e3fdcff3725841c8b04047a91733c8b98187e0b..ffae6778b471628639c41b1d5f3af08b this.handleHandSwap(map); if (!map.isEmpty()) { this.handleEquipmentChanges(map); -@@ -3377,6 +3383,10 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3364,6 +3370,10 @@ public abstract class LivingEntity extends Entity implements Attackable { @Nullable private Map collectEquipmentChanges() { @@ -135,7 +135,7 @@ index 3e3fdcff3725841c8b04047a91733c8b98187e0b..ffae6778b471628639c41b1d5f3af08b 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 { -@@ -4736,6 +4746,81 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -4723,6 +4733,81 @@ public abstract class LivingEntity extends Entity implements Attackable { return this.lastHurtByPlayerTime; } diff --git a/leaf-server/minecraft-patches/features/0119-Only-player-pushable.patch b/leaf-server/minecraft-patches/features/0119-Only-player-pushable.patch index df97992b..1f472084 100644 --- a/leaf-server/minecraft-patches/features/0119-Only-player-pushable.patch +++ b/leaf-server/minecraft-patches/features/0119-Only-player-pushable.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Only player pushable Useful for extreme cases like massive entities collide together in a small area diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java -index ffae6778b471628639c41b1d5f3af08b6b72a86c..32039b50946b23e4f55498aa09ea33cfa77b517c 100644 +index 69e91a732a497b2ffa906088a636947ce9a9ec36..d455406df1f3a4c3d1bd016d50d1d7025366ae80 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java -@@ -3644,7 +3644,7 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf +@@ -3631,7 +3631,7 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf this.checkAutoSpinAttack(boundingBox, this.getBoundingBox()); } @@ -18,7 +18,7 @@ index ffae6778b471628639c41b1d5f3af08b6b72a86c..32039b50946b23e4f55498aa09ea33cf // Paper start - Add EntityMoveEvent // Purpur start - Ridables if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) { -@@ -3782,7 +3782,14 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf +@@ -3769,7 +3769,14 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf return; } // Paper end - don't run getEntities if we're not going to use its result @@ -34,7 +34,7 @@ index ffae6778b471628639c41b1d5f3af08b6b72a86c..32039b50946b23e4f55498aa09ea33cf if (!entities.isEmpty()) { // Paper - don't run getEntities if we're not going to use its result; moved up if (_int > 0 && entities.size() > _int - 1 && this.random.nextInt(4) == 0) { -@@ -3815,6 +3822,44 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf +@@ -3802,6 +3809,44 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf } } diff --git a/leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch b/leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch index 7bee8026..7b58130e 100644 --- a/leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch +++ b/leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Optimize addOrUpdateTransientModifier diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java -index 93b375f39f10568f6b222607890a9ce67db0e9bb..69ba880e17e5cff3b2260f539683ca565f34e8dd 100644 +index ed5077708415a74da171b88fa1fb8b736446666b..62cadad97109247e65a550acc5955424b1f6fc5e 100644 --- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java @@ -107,8 +107,13 @@ public class AttributeInstance { diff --git a/leaf-server/minecraft-patches/features/0176-Optimize-AttributeMap.patch b/leaf-server/minecraft-patches/features/0176-Optimize-AttributeMap.patch deleted file mode 100644 index b22d676f..00000000 --- a/leaf-server/minecraft-patches/features/0176-Optimize-AttributeMap.patch +++ /dev/null @@ -1,100 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: hayanesuru -Date: Thu, 15 May 2025 21:11:18 +0900 -Subject: [PATCH] Optimize AttributeMap - - -diff --git a/net/minecraft/world/entity/ai/attributes/Attribute.java b/net/minecraft/world/entity/ai/attributes/Attribute.java -index f8419dde44ebc7324e783f8bee42132d5ec973c3..406767c60ec1a324faaf5d3658b161647497f99b 100644 ---- a/net/minecraft/world/entity/ai/attributes/Attribute.java -+++ b/net/minecraft/world/entity/ai/attributes/Attribute.java -@@ -16,10 +16,15 @@ public class Attribute { - private boolean syncable; - private final String descriptionId; - private Attribute.Sentiment sentiment = Attribute.Sentiment.POSITIVE; -+ // Leaf start - Optimize AttributeMap -+ public final int uid; -+ private static final java.util.concurrent.atomic.AtomicInteger SIZE = new java.util.concurrent.atomic.AtomicInteger(); -+ // Leaf end - Optimize AttributeMap - - protected Attribute(String descriptionId, double defaultValue) { - this.defaultValue = defaultValue; - this.descriptionId = descriptionId; -+ this.uid = SIZE.getAndAdd(1); // Leaf - Optimize AttributeMap - } - - public double getDefaultValue() { -diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java -index c0a09b615e9b6c4ec72b8b77a78e7da374d4498b..a550e6bd9dffa9a46f0d7967c9d73ff5cbfbaa0a 100644 ---- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java -+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java -@@ -20,7 +20,7 @@ import org.slf4j.Logger; - public class AttributeMap { - private static final Logger LOGGER = LogUtils.getLogger(); - // Gale start - Lithium - replace AI attributes with optimized collections -- private final Map, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0); -+ private final Map, AttributeInstance> attributes = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(); // Leaf - Optimize AttributeMap - // Leaf start - Multithreaded tracker - private final Set attributesToSync; - private final Set attributesToUpdate; -@@ -36,7 +36,7 @@ public class AttributeMap { - // Leaf end - Multithreaded tracker - // Gale end - Lithium - replace AI attributes with optimized collections - private final AttributeSupplier supplier; -- private final java.util.function.Function, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations -+ //private final java.util.function.Function, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations // Leaf - Optimize AttributeMap - private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables - - public AttributeMap(AttributeSupplier supplier) { -@@ -47,7 +47,7 @@ public class AttributeMap { - this.entity = entity; - // Purpur end - Ridables - this.supplier = defaultAttributes; -- this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations -+ //this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations // Leaf - Optimize AttributeMap - } - - private void onAttributeModified(AttributeInstance instance) { -@@ -71,13 +71,24 @@ public class AttributeMap { - - @Nullable - public AttributeInstance getInstance(Holder attribute) { -- if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { -- synchronized (this) { -- return this.attributes.computeIfAbsent(attribute, holder -> this.supplier.createInstance(this::onAttributeModified, (Holder) holder)); -+ // Leaf start - Optimize AttributeMap -+ AttributeInstance v; -+ if ((v = this.attributes.get(attribute)) == null) { -+ AttributeInstance newValue; -+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { -+ synchronized (this) { -+ if ((newValue = this.supplier.createInstance(this::onAttributeModified, attribute)) != null) { -+ this.attributes.put(attribute, newValue); -+ return newValue; -+ } -+ } -+ } else if ((newValue = this.supplier.createInstance(this::onAttributeModified, attribute)) != null) { -+ this.attributes.put(attribute, newValue); -+ return newValue; - } -- } else { -- return this.attributes.computeIfAbsent(attribute, holder -> this.supplier.createInstance(this::onAttributeModified, (Holder) holder)); - } -+ return v; -+ // Leaf end - Optimize AttributeMap - } - - public boolean hasAttribute(Holder attribute) { -diff --git a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java -index 24710041ccbc70e5506d8d89ae34f0141977f209..09341ef6c651150aba223689badbead490162b2b 100644 ---- a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java -+++ b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java -@@ -11,7 +11,7 @@ public class AttributeSupplier { - private final Map, AttributeInstance> instances; - - AttributeSupplier(Map, AttributeInstance> instances) { -- this.instances = instances; -+ this.instances = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(instances); // Leaf - Optimize AttributeMap - } - - public AttributeInstance getAttributeInstance(Holder attribute) { diff --git a/leaf-server/minecraft-patches/features/0177-Optimize-getScaledTrackingDistance.patch b/leaf-server/minecraft-patches/features/0176-Optimize-getScaledTrackingDistance.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0177-Optimize-getScaledTrackingDistance.patch rename to leaf-server/minecraft-patches/features/0176-Optimize-getScaledTrackingDistance.patch diff --git a/leaf-server/minecraft-patches/features/0178-Optimize-SynchedEntityData-packDirty.patch b/leaf-server/minecraft-patches/features/0177-Optimize-SynchedEntityData-packDirty.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0178-Optimize-SynchedEntityData-packDirty.patch rename to leaf-server/minecraft-patches/features/0177-Optimize-SynchedEntityData-packDirty.patch diff --git a/leaf-server/minecraft-patches/features/0179-Optimize-isEyeInFluid.patch b/leaf-server/minecraft-patches/features/0178-Optimize-isEyeInFluid.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0179-Optimize-isEyeInFluid.patch rename to leaf-server/minecraft-patches/features/0178-Optimize-isEyeInFluid.patch diff --git a/leaf-server/minecraft-patches/features/0180-Paw-optimization.patch b/leaf-server/minecraft-patches/features/0179-Paw-optimization.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0180-Paw-optimization.patch rename to leaf-server/minecraft-patches/features/0179-Paw-optimization.patch diff --git a/leaf-server/minecraft-patches/features/0181-Cache-block-path-type.patch b/leaf-server/minecraft-patches/features/0180-Cache-block-path-type.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0181-Cache-block-path-type.patch rename to leaf-server/minecraft-patches/features/0180-Cache-block-path-type.patch diff --git a/leaf-server/minecraft-patches/features/0182-optimize-getEntityStatus.patch b/leaf-server/minecraft-patches/features/0181-optimize-getEntityStatus.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0182-optimize-getEntityStatus.patch rename to leaf-server/minecraft-patches/features/0181-optimize-getEntityStatus.patch diff --git a/leaf-server/minecraft-patches/features/0183-Rail-Optimization-optimized-PoweredRailBlock-logic.patch b/leaf-server/minecraft-patches/features/0182-Rail-Optimization-optimized-PoweredRailBlock-logic.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0183-Rail-Optimization-optimized-PoweredRailBlock-logic.patch rename to leaf-server/minecraft-patches/features/0182-Rail-Optimization-optimized-PoweredRailBlock-logic.patch diff --git a/leaf-server/paper-patches/features/0023-Multithreaded-Tracker.patch b/leaf-server/paper-patches/features/0023-Multithreaded-Tracker.patch index 39c5b084..f70a9251 100644 --- a/leaf-server/paper-patches/features/0023-Multithreaded-Tracker.patch +++ b/leaf-server/paper-patches/features/0023-Multithreaded-Tracker.patch @@ -71,6 +71,19 @@ index edcd209798740f31cb302f36d7864a0d8ea1d561..e8b80ac3d2a54b9e855cad80c8a78200 set.add(connection.getPlayer().getBukkitEntity().getPlayer()); } return set; +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 8e8630db9f74e5952142dba14ec58917c5745287..f89f6cea94486842a5e9015200a0d648225c8615 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -2915,7 +2915,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + Iterator iterator = collection.iterator(); + while (iterator.hasNext()) { + AttributeInstance genericInstance = iterator.next(); +- if (genericInstance.getAttribute() == Attributes.MAX_HEALTH) { ++ if (genericInstance != null && genericInstance.getAttribute() == Attributes.MAX_HEALTH) { // Leaf - Multithreaded tracker + iterator.remove(); + break; + } diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index e52479f3c888268fd1febeb78e9965af834a8ae9..c2552c3706831f7012b5b449fa43c7d5990056a4 100644 --- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java index 9d4619c2..3464700c 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java @@ -11,7 +11,7 @@ import java.util.*; import java.util.AbstractMap.SimpleEntry; // fast array backend map with O(1) get & put & remove -public class AttributeInstanceArrayMap implements Map, AttributeInstance>, Cloneable { +public final class AttributeInstanceArrayMap implements Map, AttributeInstance>, Cloneable { private int size = 0; private transient AttributeInstance[] a = new AttributeInstance[32]; @@ -46,17 +46,17 @@ public class AttributeInstanceArrayMap implements Map, Attribu } @Override - public final int size() { + public int size() { return size; } @Override - public final boolean isEmpty() { + public boolean isEmpty() { return size == 0; } @Override - public final boolean containsKey(Object key) { + public boolean containsKey(Object key) { if (key instanceof Holder holder && holder.value() instanceof Attribute attribute) { int uid = attribute.uid; return uid >= 0 && uid < a.length && a[uid] != null; @@ -65,22 +65,22 @@ public class AttributeInstanceArrayMap implements Map, Attribu } @Override - public final boolean containsValue(Object value) { - for (final AttributeInstance instance : a) { - if (Objects.equals(value, instance)) { - return true; - } - } - return false; + public boolean containsValue(Object value) { + return value instanceof AttributeInstance val && Objects.equals(getInstance(val.getAttribute().value().uid), val); } @Override - public final AttributeInstance get(Object key) { + public AttributeInstance get(Object key) { return key instanceof Holder holder && holder.value() instanceof Attribute attribute ? a[attribute.uid] : null; } + @Nullable + public AttributeInstance getInstance(int key) { + return a[key]; + } + @Override - public final AttributeInstance put(@NotNull Holder key, AttributeInstance value) { + public AttributeInstance put(@NotNull Holder key, AttributeInstance value) { int uid = key.value().uid; AttributeInstance prev = a[uid]; setByIndex(uid, value); @@ -88,7 +88,7 @@ public class AttributeInstanceArrayMap implements Map, Attribu } @Override - public final AttributeInstance remove(Object key) { + public AttributeInstance remove(Object key) { if (!(key instanceof Holder holder) || !(holder.value() instanceof Attribute attribute)) return null; int uid = attribute.uid; AttributeInstance prev = a[uid]; @@ -97,7 +97,7 @@ public class AttributeInstanceArrayMap implements Map, Attribu } @Override - public final void putAll(@NotNull Map, ? extends AttributeInstance> m) { + public void putAll(@NotNull Map, ? extends AttributeInstance> m) { for (AttributeInstance e : m.values()) { if (e != null) { setByIndex(e.getAttribute().value().uid, e); @@ -106,13 +106,13 @@ public class AttributeInstanceArrayMap implements Map, Attribu } @Override - public final void clear() { + public void clear() { Arrays.fill(a, null); size = 0; } @Override - public final @NotNull Set> keySet() { + public @NotNull Set> keySet() { if (keys == null) { keys = new KeySet(); } @@ -120,7 +120,7 @@ public class AttributeInstanceArrayMap implements Map, Attribu } @Override - public final @NotNull Collection values() { + public @NotNull Collection values() { if (values == null) { values = new Values(); } @@ -128,7 +128,7 @@ public class AttributeInstanceArrayMap implements Map, Attribu } @Override - public final @NotNull Set, AttributeInstance>> entrySet() { + public @NotNull Set, AttributeInstance>> entrySet() { if (entries == null) { entries = new EntrySet(); } @@ -136,13 +136,23 @@ public class AttributeInstanceArrayMap implements Map, Attribu } @Override - public final boolean equals(Object o) { - if (!(o instanceof AttributeInstanceArrayMap that)) return false; - return size == that.size && Arrays.equals(a, that.a); + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof Map s)) return false; + if (s.size() != size()) return false; + if (o instanceof AttributeInstanceArrayMap that) { + return Arrays.equals(a, that.a); + } + for (Entry e : s.entrySet()) { + if (!Objects.equals(get(e.getKey()), e.getValue())) { + return false; + } + } + return true; } @Override - public final int hashCode() { + public int hashCode() { return Arrays.hashCode(a); } @@ -192,7 +202,7 @@ public class AttributeInstanceArrayMap implements Map, Attribu if (!hasNext()) throw new NoSuchElementException(); currentIndex = nextIndex; nextIndex = findNextOccupied(nextIndex + 1); - return BuiltInRegistries.ATTRIBUTE.asHolderIdMap().byIdOrThrow(currentIndex); + return BuiltInRegistries.ATTRIBUTE.get(currentIndex).orElseThrow(); } @Override @@ -279,7 +289,7 @@ public class AttributeInstanceArrayMap implements Map, Attribu public Entry, AttributeInstance> next() { if (!hasNext()) throw new NoSuchElementException(); currentIndex = nextIndex; - Holder key = BuiltInRegistries.ATTRIBUTE.asHolderIdMap().byIdOrThrow(nextIndex); + Holder key = BuiltInRegistries.ATTRIBUTE.get(nextIndex).orElseThrow(); AttributeInstance value = a[nextIndex]; nextIndex = findNextOccupied(nextIndex + 1); return new SimpleEntry<>(key, value) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceSet.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceSet.java new file mode 100644 index 00000000..5b1f32da --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceSet.java @@ -0,0 +1,113 @@ +package org.dreeam.leaf.util.map; + +import it.unimi.dsi.fastutil.ints.IntArraySet; +import it.unimi.dsi.fastutil.ints.IntSet; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Array; +import java.util.*; + +public final class AttributeInstanceSet extends AbstractCollection implements Set { + public final IntSet inner; + public final AttributeInstanceArrayMap map; + + public AttributeInstanceSet(AttributeInstanceArrayMap map) { + this.map = map; + inner = new IntArraySet(); + } + + @Override + public boolean add(AttributeInstance instance) { + return inner.add(instance.getAttribute().value().uid); + } + + @Override + public boolean remove(Object o) { + return o instanceof AttributeInstance instance && inner.remove(instance.getAttribute().value().uid); + } + + @Override + public @NotNull Iterator iterator() { + return new CloneIterator(inner.toIntArray(), map); + } + + @Override + public int size() { + return inner.size(); + } + + @Override + public boolean isEmpty() { + return inner.isEmpty(); + } + + @Override + public void clear() { + inner.clear(); + } + + @Override + public boolean contains(Object o) { + if (o instanceof AttributeInstance instance) { + return inner.contains(instance.getAttribute().value().uid); + } + return false; + } + + @Override + public AttributeInstance @NotNull [] toArray() { + int[] innerClone = inner.toIntArray(); + AttributeInstance[] arr = new AttributeInstance[innerClone.length]; + for (int i = 0; i < arr.length; i++) { + arr[i] = map.getInstance(innerClone[i]); + } + return arr; + } + + @SuppressWarnings({"unchecked"}) + @Override + public T @NotNull [] toArray(T[] a) { + if (a == null || (a.getClass() == AttributeInstance[].class && a.length == 0)) { + return (T[]) toArray(); + } + if (a.length < size()) { + a = (T[]) Array.newInstance(a.getClass().getComponentType(), size()); + } + System.arraycopy((T[]) toArray(), 0, a, 0, size()); + if (a.length > size()) { + a[size()] = null; + } + return a; + } + + static class CloneIterator implements Iterator { + private final int[] array; + private int index = 0; + private final AttributeInstanceArrayMap map; + + CloneIterator(int[] array, AttributeInstanceArrayMap map) { + this.array = array; + this.map = map; + } + + @Override + public boolean hasNext() { + return index < array.length; + } + + @Override + public AttributeInstance next() { + if (!hasNext()) throw new NoSuchElementException(); + return map.getInstance(array[index++]); + } + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof Set s)) return false; + if (s.size() != size()) return false; + return containsAll(s); + } +}