mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-19 15:09:25 +00:00
MultithreadedTracker add lock to AttributeMap and TrackedChunk
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<EquipmentSlot, ItemStack> 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<EquipmentSlot, ItemStack> collectEquipmentChanges() {
|
||||
@@ -135,7 +135,7 @@ index 0138bd4d95a592bfa5ccbb33fa6c1201f289fd2a..00233a7066d751821566b43993e8c45e
|
||||
Map<EquipmentSlot, ItemStack> map = null;
|
||||
// Paper start - EntityEquipmentChangedEvent
|
||||
record EquipmentChangeImpl(org.bukkit.inventory.ItemStack oldItem, org.bukkit.inventory.ItemStack newItem) implements io.papermc.paper.event.entity.EntityEquipmentChangedEvent.EquipmentChange {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ServerPlayer> 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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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<Holder<Attribute>, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
|
||||
+ private final Map<Holder<Attribute>, AttributeInstance> attributes = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(); // Leaf - Optimize AttributeMap
|
||||
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;
|
||||
@@ -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<Holder<Attribute>, 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> 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));
|
||||
+ // 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<Attribute>) holder));
|
||||
}
|
||||
+ return v;
|
||||
+ // Leaf end - Optimize AttributeMap
|
||||
}
|
||||
|
||||
@@ -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<ServerEntity> 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<ServerEntity> 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()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user