diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index b0a5032c..b08d943c 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -23,6 +23,28 @@ for the case of some NPC plugins which using real entity type, e.g. Citizens. But it is still recommending to use those packet based, virtual entity based NPC plugins, e.g. ZNPC Plus, Adyeshach, Fancy NPC, etc. +diff --git a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java +index 1b8193587814225c2ef2c5d9e667436eb50ff6c5..4200d22606c6a3dbdf282792a4007a51df66963b 100644 +--- a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java ++++ b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java +@@ -60,7 +60,16 @@ public final class NearbyPlayers { + + private final ServerLevel world; + private final Reference2ReferenceOpenHashMap players = new Reference2ReferenceOpenHashMap<>(); +- private final Long2ReferenceOpenHashMap byChunk = new Long2ReferenceOpenHashMap<>(); ++ // Leaf start - Multithreaded tracker ++ private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap byChunk; ++ { ++ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { ++ byChunk = it.unimi.dsi.fastutil.longs.Long2ReferenceMaps.synchronize(new Long2ReferenceOpenHashMap<>()); ++ } else { ++ byChunk = new Long2ReferenceOpenHashMap<>(); ++ } ++ } ++ // Leaf end - Multithreaded tracker + private final Long2ReferenceOpenHashMap>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES]; + { + for (int i = 0; i < this.directByChunk.length; ++i) { diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java index dd2509996bfd08e8c3f9f2be042229eac6d7692d..a35e9fae8f8da0c42f0616c4f78dc396492673aa 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java @@ -37,7 +59,7 @@ 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/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index 5d9d233e3a568aa6297ed9c703fa450f98158602..24765ca23899b2eec049bf539c1f9eafc8b48d1b 100644 +index 5d9d233e3a568aa6297ed9c703fa450f98158602..47a7bf7c38600a2ad547bbd2b7fe632e96e9a139 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -248,6 +248,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -70,14 +92,13 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..24765ca23899b2eec049bf539c1f9eaf // Paper start - optimise entity tracker if (true) { this.newTrackerTick(); -@@ -1073,7 +1089,18 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1073,7 +1089,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider final Entity entity; private final int range; SectionPos lastSectionPos; - public final Set seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl + // Leaf start - Multithreaded tracker + public static final ServerPlayerConnection[] EMPTY_OBJECT_ARRAY = new ServerPlayerConnection[0]; -+ public final Object sync = new Object(); + public final Set seenBy = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>()) : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl + private volatile ServerPlayerConnection[] seenByArray = EMPTY_OBJECT_ARRAY; + public ServerPlayerConnection[] seenBy() { @@ -90,7 +111,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..24765ca23899b2eec049bf539c1f9eaf // Paper start - optimise entity tracker private long lastChunkUpdate = -1L; -@@ -1100,27 +1127,95 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1100,27 +1126,95 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.lastTrackedChunk = chunk; final ServerPlayer[] playersRaw = players.getRawDataUnchecked(); @@ -191,7 +212,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..24765ca23899b2eec049bf539c1f9eaf if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(conn.getPlayer())) { foundToRemove = true; break; -@@ -1131,12 +1226,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1131,12 +1225,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return; } @@ -207,7 +228,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..24765ca23899b2eec049bf539c1f9eaf } @Override -@@ -1146,10 +1242,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1146,10 +1241,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (this.seenBy.isEmpty()) { return; } @@ -221,7 +242,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..24765ca23899b2eec049bf539c1f9eaf } @Override -@@ -1176,7 +1273,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1176,7 +1272,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcast(Packet packet) { @@ -230,7 +251,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..24765ca23899b2eec049bf539c1f9eaf serverPlayerConnection.send(packet); } } -@@ -1189,21 +1286,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1189,21 +1285,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcastRemoved() { @@ -268,7 +289,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..24765ca23899b2eec049bf539c1f9eaf // Paper start - remove allocation of Vec3D here // Vec3 vec3 = player.position().subtract(this.entity.position()); double vec3_dx = player.getX() - this.entity.getX(); -@@ -1231,6 +1341,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1231,6 +1340,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // CraftBukkit end if (flag) { if (this.seenBy.add(player.connection)) { @@ -276,7 +297,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..24765ca23899b2eec049bf539c1f9eaf // Paper start - entity tracking events if (io.papermc.paper.event.player.PlayerTrackEntityEvent.getHandlerList().getRegisteredListeners().length == 0 || new io.papermc.paper.event.player.PlayerTrackEntityEvent(player.getBukkitEntity(), this.entity.getBukkitEntity()).callEvent()) { this.serverEntity.addPairing(player); -@@ -1239,6 +1350,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1239,6 +1349,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.serverEntity.onPlayerAdd(); // Paper - fix desync when a player is added to the tracker } } else if (this.seenBy.remove(player.connection)) { @@ -298,18 +319,10 @@ index f106373ef3ac4a8685c2939c9e8361688a285913..51ae390c68e7a3aa193329cc3bc47ca6 public boolean visible = true; diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index 1dee20436fc29537319ee456756a8e8f7b6fe66a..1f3e030cea42a0c9e27425cb18c232f482ef8608 100644 +index 1dee20436fc29537319ee456756a8e8f7b6fe66a..bcd569e7d12d4f453c64bf12933a72c3ca362329 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java -@@ -70,6 +70,7 @@ public class ServerEntity { - private boolean wasOnGround; - @Nullable - private List> trackedDataValues; -+ public boolean wantSendDirtyEntityData = false; // Leaf - Multithreaded tracker - - // CraftBukkit start - private final Set trackedPlayers; -@@ -110,8 +111,16 @@ public class ServerEntity { +@@ -110,8 +110,16 @@ public class ServerEntity { .forEach( removedPassenger -> { if (removedPassenger instanceof ServerPlayer serverPlayer1) { @@ -328,7 +341,7 @@ index 1dee20436fc29537319ee456756a8e8f7b6fe66a..1f3e030cea42a0c9e27425cb18c232f4 } } ); -@@ -124,7 +133,7 @@ public class ServerEntity { +@@ -124,7 +132,7 @@ public class ServerEntity { MapId mapId = itemFrame.cachedMapId; // Paper - Perf: Cache map ids on item frames MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level); if (savedData != null) { @@ -337,19 +350,38 @@ index 1dee20436fc29537319ee456756a8e8f7b6fe66a..1f3e030cea42a0c9e27425cb18c232f4 final ServerPlayer serverPlayer = connection.getPlayer(); // Paper savedData.tickCarriedBy(serverPlayer, item); Packet updatePacket = savedData.getUpdatePacket(mapId, serverPlayer); -@@ -425,6 +434,12 @@ public class ServerEntity { - } +@@ -433,15 +441,30 @@ public class ServerEntity { + } - public void sendDirtyEntityData() { -+ // Leaf start - Multithreaded tracker -+ if (Thread.currentThread() instanceof org.dreeam.leaf.async.tracker.MultithreadedTracker.MultithreadedTrackerThread) { -+ wantSendDirtyEntityData = true; -+ return; -+ } -+ // Leaf end - Multithreaded tracker - SynchedEntityData entityData = this.entity.getEntityData(); - List> list = entityData.packDirty(); - if (list != null) { + 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 + this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); ++ } + } ++ // Leaf end - Multithreaded tracker + + attributesToSync.clear(); + } 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 @@ -385,6 +417,180 @@ index 327b3bc89920c4ab02c1126dc63bca05ce3abefe..1415043bee5fbbfcf9dab9184a9418d5 // Paper start - Prevent teleporting dead entities 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 +--- 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 { + } + + 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()); ++ } + ++ attributesToUpdate.clear(); ++ } ++ } else { + for (AttributeInstance attributeInstance : attributesToUpdate) { + this.onAttributeUpdated(attributeInstance.getAttribute()); + } + + attributesToUpdate.clear(); ++ } ++ // Leaf end - Multithreaded tracker + } + + protected void onAttributeUpdated(Holder attribute) { +diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java +index 8013594bb4844e7a8abf28123958e7f632d39341..93b375f39f10568f6b222607890a9ce67db0e9bb 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 { + private final Map> modifiersByOperation = Maps.newEnumMap( + AttributeModifier.Operation.class + ); +- private final Map modifierById = new Object2ObjectArrayMap<>(); +- private final Map permanentModifiers = new Object2ObjectArrayMap<>(); ++ // Leaf start - Multithreaded tracker ++ private final Map modifierById; ++ { ++ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { ++ modifierById = it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this); ++ } else { ++ modifierById = new Object2ObjectArrayMap<>(); ++ } ++ } ++ private final Map permanentModifiers; ++ { ++ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { ++ permanentModifiers = it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this); ++ } else { ++ permanentModifiers = new Object2ObjectArrayMap<>(); ++ } ++ } ++ // Leaf end - Multithreaded tracker + private double baseValue; + private boolean dirty = true; + private double cachedValue; +@@ -54,7 +70,13 @@ public class AttributeInstance { + + @VisibleForTesting + Map getModifiers(AttributeModifier.Operation operation) { +- return this.modifiersByOperation.computeIfAbsent(operation, operation1 -> new Object2ObjectOpenHashMap<>()); ++ // Leaf start - Multithreaded tracker ++ return this.modifiersByOperation.computeIfAbsent(operation, operation1 -> { ++ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) ++ return it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this); ++ else return new Object2ObjectArrayMap<>(); ++ }); ++ // Leaf end - Multithreaded tracker + } + + public Set getModifiers() { +@@ -174,6 +196,13 @@ 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 { + if (!this.permanentModifiers.isEmpty()) { + ListTag listTag = new ListTag(); + +- for (AttributeModifier attributeModifier : this.permanentModifiers.values()) { +- listTag.add(attributeModifier.save()); ++ // Leaf start - Multithreaded tracker ++ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { ++ synchronized (this) { ++ for (AttributeModifier attributeModifier : this.permanentModifiers.values()) { ++ listTag.add(attributeModifier.save()); ++ } ++ } ++ } else { ++ for (AttributeModifier attributeModifier : this.permanentModifiers.values()) { ++ listTag.add(attributeModifier.save()); ++ } + } ++ // Leaf end - Multithreaded tracker + + 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 +--- 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 { + 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 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); ++ } ++ } ++ // 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 { + + @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)); ++ } ++ } else { ++ return this.attributes.computeIfAbsent(attribute, holder -> this.supplier.createInstance(this::onAttributeModified, (Holder) holder)); ++ } + } + + public boolean hasAttribute(Holder attribute) { +@@ -176,8 +193,17 @@ public class AttributeMap { + + // 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 + } + // Paper - end - living entity allow attribute registration + 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 e058cc2f..23754ae5 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 316242d60db43494300a29b7d0945d0d76ac9987..0138bd4d95a592bfa5ccbb33fa6c1201f289fd2a 100644 +index 324f8952a921a3897f4ff48145f0f8645c690318..3e3fdcff3725841c8b04047a91733c8b98187e0b 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java -@@ -2739,6 +2739,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -2752,6 +2752,7 @@ public abstract class LivingEntity extends Entity implements Attackable { } protected void updateSwingTime() { @@ -23,7 +23,7 @@ index 316242d60db43494300a29b7d0945d0d76ac9987..0138bd4d95a592bfa5ccbb33fa6c1201 int currentSwingDuration = this.getCurrentSwingDuration(); if (this.swinging) { this.swingTime++; -@@ -3690,6 +3691,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3703,6 +3704,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 9c174967..ba404481 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 0138bd4d95a592bfa5ccbb33fa6c1201f289fd2a..00233a7066d751821566b43993e8c45e7dad95d0 100644 +index 3e3fdcff3725841c8b04047a91733c8b98187e0b..ffae6778b471628639c41b1d5f3af08b6b72a86c 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 0138bd4d95a592bfa5ccbb33fa6c1201f289fd2a..00233a7066d751821566b43993e8c45e Equippable equippable = newItem.get(DataComponents.EQUIPPABLE); if (!this.isSilent() && equippable != null && slot == equippable.slot() && !silent) { // CraftBukkit this.level() -@@ -3355,6 +3360,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3368,6 +3373,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 0138bd4d95a592bfa5ccbb33fa6c1201f289fd2a..00233a7066d751821566b43993e8c45e this.handleHandSwap(map); if (!map.isEmpty()) { this.handleEquipmentChanges(map); -@@ -3364,6 +3370,10 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3377,6 +3383,10 @@ public abstract class LivingEntity extends Entity implements Attackable { @Nullable private Map collectEquipmentChanges() { @@ -135,7 +135,7 @@ index 0138bd4d95a592bfa5ccbb33fa6c1201f289fd2a..00233a7066d751821566b43993e8c45e 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 { -@@ -4723,6 +4733,81 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -4736,6 +4746,81 @@ public abstract class LivingEntity extends Entity implements Attackable { return this.lastHurtByPlayerTime; } diff --git a/leaf-server/minecraft-patches/features/0105-Cache-chunk-key.patch b/leaf-server/minecraft-patches/features/0105-Cache-chunk-key.patch index 07654988..38406bc0 100644 --- a/leaf-server/minecraft-patches/features/0105-Cache-chunk-key.patch +++ b/leaf-server/minecraft-patches/features/0105-Cache-chunk-key.patch @@ -9,10 +9,10 @@ This patch didn't cahce SectionPos or BlockPos to chunkKey, since it needs to co TODO: Cache block pos and section pos, whether need? diff --git a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java -index 1b8193587814225c2ef2c5d9e667436eb50ff6c5..288a3eb57f3431dd624ad8a4b08684563abbc5ad 100644 +index 4200d22606c6a3dbdf282792a4007a51df66963b..4b258f048c73107d0d050a9aa4b4a39788145b17 100644 --- a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java +++ b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java -@@ -127,7 +127,7 @@ public final class NearbyPlayers { +@@ -136,7 +136,7 @@ public final class NearbyPlayers { } public TrackedChunk getChunk(final ChunkPos pos) { @@ -21,7 +21,7 @@ index 1b8193587814225c2ef2c5d9e667436eb50ff6c5..288a3eb57f3431dd624ad8a4b0868456 } public TrackedChunk getChunk(final BlockPos pos) { -@@ -143,7 +143,7 @@ public final class NearbyPlayers { +@@ -152,7 +152,7 @@ public final class NearbyPlayers { } public ReferenceList getPlayers(final ChunkPos pos, final NearbyMapType type) { @@ -84,7 +84,7 @@ index 571db5f9bf94745a8afe2cd313e593fb15db5e37..1487b7d8be435b3fbad2aabd05796965 valueInMap = new ServerChunkTasks( keyInMap, ServerLightQueue.this.lightInterface, ServerLightQueue.this, priority diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 49cbdf014d0626b36eb4c451b6de09508822b7fd..a9e7424bb55266c5e04c56dcf598ce7d149eeb21 100644 +index a669a59a42f814480879a52d2da5e04c636720de..61afe93ff7f6f6ac3967e948bf39b0ab559e2808 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -508,7 +508,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe 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 6b8b3ffa..df97992b 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 00233a7066d751821566b43993e8c45e7dad95d0..03c9edad5c2f5e902b7a766c6d0be61bf3c263ae 100644 +index ffae6778b471628639c41b1d5f3af08b6b72a86c..32039b50946b23e4f55498aa09ea33cfa77b517c 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java -@@ -3631,7 +3631,7 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf +@@ -3644,7 +3644,7 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf this.checkAutoSpinAttack(boundingBox, this.getBoundingBox()); } @@ -18,7 +18,7 @@ index 00233a7066d751821566b43993e8c45e7dad95d0..03c9edad5c2f5e902b7a766c6d0be61b // 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()) { -@@ -3769,7 +3769,14 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf +@@ -3782,7 +3782,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 00233a7066d751821566b43993e8c45e7dad95d0..03c9edad5c2f5e902b7a766c6d0be61b 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) { -@@ -3802,6 +3809,44 @@ public abstract class LivingEntity extends Entity implements Attackable, net.caf +@@ -3815,6 +3822,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 087595bb..7bee8026 100644 --- a/leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch +++ b/leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch @@ -5,10 +5,10 @@ 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 8013594bb4844e7a8abf28123958e7f632d39341..7505485c8965e5492a9d68288596178cfe0971ee 100644 +index 93b375f39f10568f6b222607890a9ce67db0e9bb..69ba880e17e5cff3b2260f539683ca565f34e8dd 100644 --- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java -@@ -85,8 +85,13 @@ public class AttributeInstance { +@@ -107,8 +107,13 @@ public class AttributeInstance { } public void addOrUpdateTransientModifier(AttributeModifier modifier) { diff --git a/leaf-server/minecraft-patches/features/0163-Protocol-Core.patch b/leaf-server/minecraft-patches/features/0163-Protocol-Core.patch index 59c3cd8d..67e20d21 100644 --- a/leaf-server/minecraft-patches/features/0163-Protocol-Core.patch +++ b/leaf-server/minecraft-patches/features/0163-Protocol-Core.patch @@ -34,10 +34,10 @@ index 98af1ad020a003db66d7319f33d43deec315aec5..9669036e6b7f1830888e48c99acb01d4 for (int i = 0; i < this.tickables.size(); i++) { this.tickables.get(i).run(); diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index e67d87b3043b381d27f75f37e3b7f922e18dcc2d..d64434d808d164ab1201f244058e2964860a590c 100644 +index bcd569e7d12d4f453c64bf12933a72c3ca362329..b48032bc878e11518d63c128edeef6bf3770f7d1 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java -@@ -284,6 +284,7 @@ public class ServerEntity { +@@ -283,6 +283,7 @@ public class ServerEntity { this.entity.hurtMarked = false; this.broadcastAndSend(new ClientboundSetEntityMotionPacket(this.entity)); } diff --git a/leaf-server/minecraft-patches/features/0176-Optimize-AttributeMap.patch b/leaf-server/minecraft-patches/features/0176-Optimize-AttributeMap.patch index da4562d9..b22d676f 100644 --- a/leaf-server/minecraft-patches/features/0176-Optimize-AttributeMap.patch +++ b/leaf-server/minecraft-patches/features/0176-Optimize-AttributeMap.patch @@ -25,17 +25,20 @@ index f8419dde44ebc7324e783f8bee42132d5ec973c3..406767c60ec1a324faaf5d3658b16164 public double getDefaultValue() { diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java -index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..51a5a3a804a1cbb0e1d23be432043552b102d837 100644 +index c0a09b615e9b6c4ec72b8b77a78e7da374d4498b..a550e6bd9dffa9a46f0d7967c9d73ff5cbfbaa0a 100644 --- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java -@@ -20,12 +20,12 @@ import org.slf4j.Logger; +@@ -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 - 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; +@@ -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 @@ -43,7 +46,7 @@ index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..51a5a3a804a1cbb0e1d23be432043552 private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables public AttributeMap(AttributeSupplier supplier) { -@@ -36,7 +36,7 @@ public class AttributeMap { +@@ -47,7 +47,7 @@ public class AttributeMap { this.entity = entity; // Purpur end - Ridables this.supplier = defaultAttributes; @@ -52,20 +55,31 @@ index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..51a5a3a804a1cbb0e1d23be432043552 } private void onAttributeModified(AttributeInstance instance) { -@@ -60,7 +60,17 @@ public class AttributeMap { +@@ -71,13 +71,24 @@ public class AttributeMap { @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 - Optimize AttributeMap + AttributeInstance v; + if ((v = this.attributes.get(attribute)) == null) { + AttributeInstance newValue; -+ if ((newValue = this.supplier.createInstance(this::onAttributeModified, attribute)) != null) { ++ 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 } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java index e1ce6caa..7ef71a0f 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java @@ -32,15 +32,6 @@ public class MultithreadedTracker { private static long lastWarnMillis = System.currentTimeMillis(); private static ThreadPoolExecutor TRACKER_EXECUTOR = null; - private record SendChanges(ServerEntity[] entities, int size) implements Runnable { - @Override - public void run() { - for (int i = 0; i < size; i++) { - entities[i].sendDirtyEntityData(); - } - } - } - private MultithreadedTracker() { } @@ -80,7 +71,6 @@ public class MultithreadedTracker { // Move tracking to off-main TRACKER_EXECUTOR.execute(() -> { - ReferenceArrayList sendDirty = ReferenceArrayList.wrap(new ServerEntity[0]); for (final Entity entity : trackerEntitiesRaw) { if (entity == null) continue; @@ -88,19 +78,12 @@ public class MultithreadedTracker { if (tracker == null) continue; - // Don't Parallel Tick Tracker of Entity - synchronized (tracker.sync) { - tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); + synchronized (tracker) { + var trackedChunk = nearbyPlayers.getChunk(entity.chunkPosition()); + tracker.moonrise$tick(trackedChunk); tracker.serverEntity.sendChanges(); - if (tracker.serverEntity.wantSendDirtyEntityData) { - tracker.serverEntity.wantSendDirtyEntityData = false; - sendDirty.add(tracker.serverEntity); - } } } - if (!sendDirty.isEmpty()) { - level.getServer().execute(new SendChanges(sendDirty.elements(), sendDirty.size())); - } }); } @@ -121,7 +104,7 @@ public class MultithreadedTracker { if (tracker == null) continue; - synchronized (tracker.sync) { + synchronized (tracker) { tickTask[index] = tracker.leafTickCompact(nearbyPlayers.getChunk(entity.chunkPosition())); sendChangesTasks[index] = () -> tracker.serverEntity.sendChanges(); // Collect send changes to task array } @@ -140,22 +123,6 @@ public class MultithreadedTracker { sendChanges.run(); } - - ReferenceArrayList sendDirty = ReferenceArrayList.wrap(new ServerEntity[0]);; - for (final Entity entity : trackerEntitiesRaw) { - if (entity == null) continue; - - final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity) entity).moonrise$getTrackedEntity(); - - if (tracker == null) continue; - if (tracker.serverEntity.wantSendDirtyEntityData) { - tracker.serverEntity.wantSendDirtyEntityData = false; - sendDirty.add(tracker.serverEntity); - } - } - if (!sendDirty.isEmpty()) { - level.getServer().execute(new SendChanges(sendDirty.elements(), sendDirty.size())); - } }); }