9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2026-01-06 15:51:31 +00:00

MultithreadedTracker add lock to AttributeMap and TrackedChunk

This commit is contained in:
hayanesuru
2025-05-29 12:20:52 +09:00
parent a0457c6ae3
commit df1d62bed9
9 changed files with 286 additions and 99 deletions

View File

@@ -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<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
- private final Long2ReferenceOpenHashMap<TrackedChunk> byChunk = new Long2ReferenceOpenHashMap<>();
+ // Leaf start - Multithreaded tracker
+ private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap<TrackedChunk> 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<ReferenceList<ServerPlayer>>[] 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<ServerPlayerConnection> 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<ServerPlayerConnection> 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<SynchedEntityData.DataValue<?>> trackedDataValues;
+ public boolean wantSendDirtyEntityData = false; // Leaf - Multithreaded tracker
// CraftBukkit start
private final Set<net.minecraft.server.network.ServerPlayerConnection> 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<SynchedEntityData.DataValue<?>> list = entityData.packDirty();
if (list != null) {
if (this.entity instanceof LivingEntity) {
- Set<AttributeInstance> attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync();
+ // Leaf start - Multithreaded tracker
+ var attributeMap = ((LivingEntity)this.entity).getAttributes();
+ Set<AttributeInstance> 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<AttributeInstance> attributesToUpdate = this.getAttributes().getAttributesToUpdate();
+ // Leaf start - Multithreaded tracker
+ var attributeMap = this.getAttributes();
+ Set<AttributeInstance> 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> 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<AttributeModifier.Operation, Map<ResourceLocation, AttributeModifier>> modifiersByOperation = Maps.newEnumMap(
AttributeModifier.Operation.class
);
- private final Map<ResourceLocation, AttributeModifier> modifierById = new Object2ObjectArrayMap<>();
- private final Map<ResourceLocation, AttributeModifier> permanentModifiers = new Object2ObjectArrayMap<>();
+ // Leaf start - Multithreaded tracker
+ private final Map<ResourceLocation, AttributeModifier> 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<ResourceLocation, AttributeModifier> 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<ResourceLocation, AttributeModifier> 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<AttributeModifier> 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<Holder<Attribute>, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
- private final Set<AttributeInstance> attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
- private final Set<AttributeInstance> attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
+ // Leaf start - Multithreaded tracker
+ private final Set<AttributeInstance> attributesToSync;
+ private final Set<AttributeInstance> 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<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations
@@ -60,7 +71,13 @@ public class AttributeMap {
@Nullable
public AttributeInstance getInstance(Holder<Attribute> 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<Attribute>) holder));
+ }
+ } else {
+ return this.attributes.computeIfAbsent(attribute, holder -> this.supplier.createInstance(this::onAttributeModified, (Holder<Attribute>) holder));
+ }
}
public boolean hasAttribute(Holder<Attribute> attribute) {
@@ -176,8 +193,17 @@ public class AttributeMap {
// Paper - start - living entity allow attribute registration
public void registerAttribute(Holder<Attribute> 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