9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-28 11:29:11 +00:00
Files
Leaf/leaf-server/minecraft-patches/features/0303-Multithreaded-Tracker.patch
hayanesuru f2682d84a6 optimize attribute and cleanup entity tracker (#548)
* optimize attribute

* compile fix

* redo async tracker

* rename id

* refactor

* fix comment

* reduce call

* fix entity removal

* rename

* fix

* fix ctx

* unnecessary

* rebuild patches

* fix immediately remove

closes: #555

* wrap
2025-12-10 17:00:14 -05:00

1213 lines
67 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: peaches94 <peachescu94@gmail.com>
Date: Sat, 2 Jul 2022 00:35:56 -0500
Subject: [PATCH] Multithreaded Tracker
Original license: GPL v3
Original project: https://github.com/Bloom-host/Petal
Original license: GPL v3
Original project: https://github.com/TECHNOVE/Airplane-Experimental
Co-authored-by: Paul Sauve <paul@technove.co>
Co-authored-by: Kevin Raneri <kevin.raneri@gmail.com>
Co-authored-by: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com>
Co-authored-by: hayanesuru <hayanesuru@outlook.jp>
This patch refactored from original multithreaded tracker (Petal version),
and is derived from the Airplane fork by Paul Sauve, the tree is like:
Airplane -> Pufferfish(?) -> Petal -> Leaf
The core logic has beed reworked compared to the old one, can handle larger
scale situation better now.
Current impl includes many improvements and fixes we made, such as
plugin compat issues with some NPC plugins using real entity type,
e.g. Citizens.
However we still recommend to use those packet 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 288a3eb57f3431dd624ad8a4b08684563abbc5ad..499cad369242f9ad724b3251538d62d8dc8d2ec8 100644
--- a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
+++ b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
@@ -164,6 +164,8 @@ public final class NearbyPlayers {
private int nonEmptyLists;
private long updateCount;
+ public final it.unimi.dsi.fastutil.objects.ReferenceSet<ServerPlayer> playersTracking = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(1)) : null; // Leaf - Multithreaded tracker
+ public final java.util.concurrent.atomic.AtomicLong trackingUpdateCountAtomic = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? new java.util.concurrent.atomic.AtomicLong(0) : null; // Leaf - Multithreaded tracker
public TrackedChunk(final long chunkKey, final NearbyPlayers nearbyPlayers) {
this.chunkKey = chunkKey;
this.nearbyPlayers = nearbyPlayers;
@@ -177,6 +179,12 @@ public final class NearbyPlayers {
return this.updateCount;
}
+ // Leaf start - Multithreaded tracker
+ public long getAtomicUpdateCount() {
+ return this.trackingUpdateCountAtomic.get() | 0x1000000000000000L;
+ }
+ // Leaf end - Multithreaded tracker
+
public ReferenceList<ServerPlayer> getPlayers(final NearbyMapType type) {
return this.players[type.ordinal()];
}
@@ -185,6 +193,12 @@ public final class NearbyPlayers {
++this.updateCount;
final int idx = type.ordinal();
+ // Leaf start - Multithreaded tracker
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled && idx == NearbyMapType.VIEW_DISTANCE.ordinal()) {
+ this.trackingUpdateCountAtomic.getAndIncrement();
+ this.playersTracking.add(player);
+ }
+ // Leaf end - Multithreaded tracker
final ReferenceList<ServerPlayer> list = this.players[idx];
if (list == null) {
++this.nonEmptyLists;
@@ -203,6 +217,12 @@ public final class NearbyPlayers {
++this.updateCount;
final int idx = type.ordinal();
+ // Leaf start - Multithreaded tracker
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled && type == NearbyMapType.VIEW_DISTANCE) {
+ this.trackingUpdateCountAtomic.getAndIncrement();
+ this.playersTracking.remove(player);
+ }
+ // Leaf end - Multithreaded tracker
final ReferenceList<ServerPlayer> list = this.players[idx];
if (list == null) {
throw new IllegalStateException("Does not contain player " + player);
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
index 808c487e3dda65decb124981ebee75b9e36ccc7c..24f0d13922748eb8a96fd83f14204984becd2513 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
@@ -345,7 +345,7 @@ public final class RegionizedPlayerChunkLoader {
private boolean canGenerateChunks = true;
private final ArrayDeque<ChunkHolderManager.TicketOperation<?, ?>> delayedTicketOps = new ArrayDeque<>();
- private final LongOpenHashSet sentChunks = new LongOpenHashSet();
+ private final LongOpenHashSet sentChunks = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? new org.dreeam.leaf.util.map.SyncLongOpenHashSet() : new LongOpenHashSet(); // Leaf - Multithreaded tracker
private static final byte CHUNK_TICKET_STAGE_NONE = 0;
private static final byte CHUNK_TICKET_STAGE_LOADING = 1;
@@ -423,6 +423,14 @@ public final class RegionizedPlayerChunkLoader {
PlatformHooks.get().onChunkWatch(this.world, chunk, this.player);
PlayerChunkSender.sendChunk(this.player.connection, this.world, chunk);
+ // Leaf start - Multithreaded tracker
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
+ ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk trackedChunk = chunk.moonrise$getChunkHolder().holderData.nearbyPlayers;
+ if (trackedChunk != null) {
+ trackedChunk.trackingUpdateCountAtomic.getAndIncrement();
+ }
+ }
+ // Leaf end - Multithreaded tracker
return;
}
throw new IllegalStateException();
diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java
index df6fbb35e5023b42de0b97434712e04a6b3e66a3..83b29ec533158cc04cc1bab1fe96c1235778a2cd 100644
--- a/io/papermc/paper/FeatureHooks.java
+++ b/io/papermc/paper/FeatureHooks.java
@@ -85,10 +85,22 @@ public final class FeatureHooks {
final LongOpenHashSet rawChunkKeys = player.moonrise$getChunkLoader().getSentChunksRaw();
final ObjectSet<org.bukkit.Chunk> chunks = new ObjectOpenHashSet<>(rawChunkKeys.size());
final World world = player.level().getWorld();
- final LongIterator iter = player.moonrise$getChunkLoader().getSentChunksRaw().longIterator();
- while (iter.hasNext()) {
- chunks.add(world.getChunkAt(iter.nextLong(), false));
+ // Leaf start - Multithreaded tracker
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
+ final LongOpenHashSet set = player.moonrise$getChunkLoader().getSentChunksRaw();
+ synchronized (set) {
+ final LongIterator iter = set.longIterator();
+ while (iter.hasNext()) {
+ chunks.add(world.getChunkAt(iter.nextLong(), false));
+ }
+ }
+ } else {
+ final LongIterator iter = player.moonrise$getChunkLoader().getSentChunksRaw().longIterator();
+ while (iter.hasNext()) {
+ chunks.add(world.getChunkAt(iter.nextLong(), false));
+ }
}
+ // Leaf end - Multithreaded tracker
// Paper end - rewrite chunk system
return ObjectSets.unmodifiable(chunks);
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index abd930f4a23483e50aa567bda3744fd805c0a50c..a19eee14eccaa6cbff42b054d803a41a4f00443f 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1798,6 +1798,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Leaf end - SparklyPaper - parallel world ticking
this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { for (ServerLevel world : getAllLevels()) { world.leaf$asyncTracker.onTickEnd(); } } // Leaf - Multithreaded tracker
this.tickConnection();
this.playerList.tick();
if (this.tickRateManager.runsNormally()) {
@@ -1877,6 +1878,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
Map<ResourceKey<Level>, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels);
newLevels.remove(level.dimension());
if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) level.tickExecutor.shutdown(); // Leaf - SparklyPaper - parallel world ticking (We remove it in here instead of ServerLevel.close() because ServerLevel.close() is never called!)
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { level.leaf$asyncTracker.onTickEnd(); } // Leaf - Multithreaded tracker
this.levels = Collections.unmodifiableMap(newLevels);
}
// CraftBukkit end
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 2dca1ceb886537ff96d0834844429e1c7b97bd79..e102536a7587a888cd1cd1989b8089367613dddb 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -1026,6 +1026,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end - optimise entity tracker
protected void tick() {
+ // Leaf start - Multithreaded tracker
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
+ final ServerLevel level = this.level;
+ level.leaf$asyncTracker.tick(level);
+ return;
+ }
+ // Leaf end - Multithreaded tracker
// Paper start - optimise entity tracker
if (true) {
this.newTrackerTick();
@@ -1148,12 +1155,60 @@ 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];
+
+ private class SeenBySet extends it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<ServerPlayerConnection> {
+ @Override
+ public boolean add(ServerPlayerConnection serverPlayerConnection) {
+ if (super.add(serverPlayerConnection)) {
+ // for plugin compatibility
+ TrackedEntity.this.seenByUpdated = true;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean remove(Object k) {
+ if (super.remove(k)) {
+ // for plugin compatibility
+ TrackedEntity.this.seenByUpdated = true;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void clear() {
+ TrackedEntity.this.seenByUpdated = true;
+ super.clear();
+ }
+ }
+
+ public final Set<ServerPlayerConnection> seenBy = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new SeenBySet()) : new SeenBySet(); // Paper - Perf: optimise map impl
+ private volatile boolean seenByUpdated = false;
+ private volatile ServerPlayerConnection[] seenByArray = EMPTY_OBJECT_ARRAY;
+ public ServerPlayerConnection[] seenBy() {
+ if (!seenByUpdated) {
+ return seenByArray;
+ } else {
+ return seenBy.toArray(EMPTY_OBJECT_ARRAY);
+ }
+ }
+ public void seenByUpdated() {
+ this.seenByArray = this.seenBy.toArray(EMPTY_OBJECT_ARRAY);
+ seenByUpdated = false;
+ }
+ // Leaf end - Multithreaded tracker
// Paper start - optimise entity tracker
private long lastChunkUpdate = -1L;
private ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk lastTrackedChunk;
+ // Leaf - Multithreaded tracker - diff on change
@Override
public final void moonrise$tick(final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk chunk) {
if (chunk == null) {
@@ -1163,6 +1218,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final ca.spottedleaf.moonrise.common.list.ReferenceList<ServerPlayer> players = chunk.getPlayers(ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.VIEW_DISTANCE);
+ // Leaf - Multithreaded tracker - diff on change
if (players == null) {
this.moonrise$clearPlayers();
return;
@@ -1175,27 +1231,117 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.lastTrackedChunk = chunk;
final ServerPlayer[] playersRaw = players.getRawDataUnchecked();
-
- for (int i = 0, len = players.size(); i < len; ++i) {
+ // Leaf start - Multithreaded tracker
+ final int playersLength = Math.min(playersRaw.length, players.size());
+ boolean updated = false;
+ for (int i = 0; i < playersLength; ++i) {
final ServerPlayer player = playersRaw[i];
- this.updatePlayer(player);
+ updated |= this.updatePlayerMulti(player);
}
+ if (updated) seenByUpdated();
+ // Leaf end - Multithreaded tracker
if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) {
// need to purge any players possible not in the chunk list
- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
+ // Leaf start - Multithreaded tracker
+ boolean removed = false;
+ for (final ServerPlayerConnection conn : this.seenBy()) {
final ServerPlayer player = conn.getPlayer();
if (!players.contains(player)) {
- this.removePlayer(player);
+ removed |= this.removePlayerMulti(player);
+ }
+ }
+ if (removed) this.seenByUpdated();
+ // Leaf end - Multithreaded tracker
+ }
+ }
+
+ // Leaf start - Multithreaded tracker
+ public final boolean leaf$tick(final org.dreeam.leaf.async.tracker.TrackerCtx ctx, final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk chunk) {
+ if (chunk == null || chunk.playersTracking.isEmpty()) {
+ this.lastChunkUpdate = -1L;
+ this.lastTrackedChunk = null;
+ for (final ServerPlayerConnection conn : this.seenBy()) {
+ if (this.seenBy.remove(conn)) {
+ ctx.stopSeenByPlayer(conn, this.entity);
+ }
+ }
+ this.seenByUpdated();
+ return false;
+ }
+
+ final it.unimi.dsi.fastutil.objects.ReferenceSet<ServerPlayer> players = chunk.playersTracking;
+ final long currChunkUpdate = chunk.getAtomicUpdateCount();
+ final boolean chunkStateChanged = this.lastChunkUpdate != currChunkUpdate || this.lastTrackedChunk != chunk;
+ this.lastChunkUpdate = currChunkUpdate;
+ this.lastTrackedChunk = chunk;
+
+ if (!(chunkStateChanged || ((entity.tickCount + entity.getId()) & 15) == 15)) {
+ return this.seenBy().length != 0;
+ }
+
+ boolean updated = false;
+ final double ex = this.entity.getX();
+ final double ey = this.entity.getY();
+ final double ez = this.entity.getZ();
+ final int eChunkX = this.entity.chunkPosition().x;
+ final int eChunkZ = this.entity.chunkPosition().z;
+ final double effectiveRange = this.getEffectiveRange();
+ final double rangeSqr = effectiveRange * effectiveRange;
+ final double rangeY = level.paperConfig().entities.trackingRangeY.enabled ? level.paperConfig().entities.trackingRangeY.get(this.entity, -1) : -1;
+ final double rangeYSqr = (rangeY > 0.0) ? (rangeY * rangeY) : 0.0;
+ synchronized (players) {
+ for (ServerPlayer player : players) {
+ if (player == this.entity) {
+ continue;
+ }
+ Vec3 playerPos = player.position();
+ final double dx = playerPos.x - ex;
+ final double dz = playerPos.z - ez;
+ final double dy = playerPos.y - ey;
+ final double playerViewDistance = ChunkMap.this.getPlayerViewDistance(player);
+ final boolean flag = ((dx * dx + dz * dz) <= Math.min(rangeSqr, playerViewDistance * playerViewDistance * 256.0))
+ && ((rangeYSqr == 0.0) || ((dy * dy) <= rangeYSqr))
+ && this.entity.broadcastToPlayer(player)
+ && ChunkMap.this.isChunkTracked(player, eChunkX, eChunkZ)
+ && player.getBukkitEntity().canSeeChunkMapUpdatePlayer(this.entity.getBukkitEntity());
+ if (flag) {
+ if (this.seenBy.add(player.connection)) {
+ ctx.startSeenByPlayer(player.connection, this.serverEntity.entity);
+ this.serverEntity.onPlayerAdd();
+ updated = true;
+ }
+ } else if (this.seenBy.remove(player.connection)) {
+ ctx.stopSeenByPlayer(player.connection, this.entity);
+ updated = true;
}
}
}
+ if (updated) {
+ this.seenByUpdated();
+ }
+ if (!chunkStateChanged) {
+ return this.seenBy().length != 0;
+ }
+ updated = false;
+ for (final ServerPlayerConnection conn : this.seenBy()) {
+ final ServerPlayer player = conn.getPlayer();
+ if (!players.contains(player) && this.seenBy.remove(conn)) {
+ ctx.stopSeenByPlayer(conn, this.entity);
+ updated = true;
+ }
+ }
+ if (updated) {
+ this.seenByUpdated();
+ }
+ return this.seenBy().length != 0;
}
+ // Leaf end - Multithreaded tracker
@Override
public final void moonrise$removeNonTickThreadPlayers() {
boolean foundToRemove = false;
- for (final ServerPlayerConnection conn : this.seenBy) {
+ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf - Multithreaded tracker
if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(conn.getPlayer())) {
foundToRemove = true;
break;
@@ -1206,12 +1352,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return;
}
- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
+ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf - Multithreaded tracker
ServerPlayer player = conn.getPlayer();
if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(player)) {
- this.removePlayer(player);
+ this.removePlayerMulti(player); // Leaf - Multithreaded tracker
}
}
+ this.seenByUpdated(); // Leaf - Multithreaded tracker
}
@Override
@@ -1221,10 +1368,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (this.seenBy.isEmpty()) {
return;
}
- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
+ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf - Multithreaded tracker
ServerPlayer player = conn.getPlayer();
- this.removePlayer(player);
+ this.removePlayerMulti(player); // Leaf - Multithreaded tracker
}
+ this.seenByUpdated(); // Leaf - Multithreaded tracker
}
@Override
@@ -1251,13 +1399,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void broadcast(Packet<?> packet) {
- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) {
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) { // Leaf - petal - Multithreaded tracker
serverPlayerConnection.send(packet);
}
}
public void broadcastIgnorePlayers(Packet<?> packet, List<UUID> ignoredPlayers) {
- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) {
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) { // Leaf - petal - Multithreaded tracker
if (!ignoredPlayers.contains(serverPlayerConnection.getPlayer().getUUID())) {
serverPlayerConnection.send(packet);
}
@@ -1272,15 +1420,27 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void broadcastRemoved() {
- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) {
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) { // Leaf - petal - Multithreaded tracker
this.serverEntity.removePairing(serverPlayerConnection.getPlayer());
}
}
+ // Leaf start - Multithreaded tracker
+ public boolean removePlayerMulti(ServerPlayer player) {
+ if (this.seenBy.remove(player.connection)) {
+ this.serverEntity.removePairing(player);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // Leaf end - Multithreaded tracker
+
public void removePlayer(ServerPlayer player) {
org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot
if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player);
+ this.seenByUpdated(); // Leaf - Multithreaded tracker
}
}
@@ -1314,6 +1474,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// CraftBukkit end
if (flag) {
if (this.seenBy.add(player.connection)) {
+ this.seenByUpdated(); // Leaf - Multithreaded tracker
// 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);
@@ -1321,11 +1482,60 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end - entity tracking events
this.serverEntity.onPlayerAdd(); // Paper - fix desync when a player is added to the tracker
}
+ } else if (this.seenBy.remove(player.connection)) {
+ this.seenByUpdated(); // Leaf - Multithreaded tracker
+ this.serverEntity.removePairing(player);
+ }
+ }
+ }
+
+ // Leaf start - Multithreaded tracker
+ private boolean updatePlayerMulti(ServerPlayer player) {
+ org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
+ if (player != this.entity) {
+ // Paper start - remove allocation of Vec3D here
+ // Vec3 vec3 = player.position().subtract(this.entity.position());
+ double vec3_dx = player.getX() - this.entity.getX();
+ double vec3_dz = player.getZ() - this.entity.getZ();
+ // Paper end - remove allocation of Vec3D here
+ int playerViewDistance = ChunkMap.this.getPlayerViewDistance(player);
+ double d = Math.min(this.getEffectiveRange(), playerViewDistance * 16);
+ double d1 = vec3_dx * vec3_dx + vec3_dz * vec3_dz; // Paper
+ double d2 = d * d;
+ // Paper start - Configurable entity tracking range by Y
+ boolean flag = d1 <= d2;
+ if (flag && level.paperConfig().entities.trackingRangeY.enabled) {
+ double rangeY = level.paperConfig().entities.trackingRangeY.get(this.entity, -1);
+ if (rangeY != -1) {
+ double vec3_dy = player.getY() - this.entity.getY();
+ flag = vec3_dy * vec3_dy <= rangeY * rangeY;
+ }
+ }
+ flag = flag && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
+ // Paper end - Configurable entity tracking range by Y
+ // CraftBukkit start - respect vanish API
+ if (flag && !player.getBukkitEntity().canSeeChunkMapUpdatePlayer(this.entity.getBukkitEntity())) { // Paper - only consider hits // SparklyPaper - optimize canSee checks
+ flag = false;
+ }
+ // CraftBukkit end
+ if (flag) {
+ if (this.seenBy.add(player.connection)) {
+ // 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);
+ }
+ // Paper end - entity tracking events
+ this.serverEntity.onPlayerAdd(); // Paper - fix desync when a player is added to the tracker
+ return true;
+ }
} else if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player);
+ return true;
}
}
+ return false;
}
+ // Leaf end - Multithreaded tracker
private int scaledRange(int trackingDistance) {
return ChunkMap.this.level.getServer().getScaledTrackingDistance(trackingDistance);
@@ -1352,10 +1562,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end - optimise entity tracker
}
+ // Leaf start - Multithreaded tracker
public void updatePlayers(List<ServerPlayer> playersList) {
+ boolean updated = false;
for (ServerPlayer serverPlayer : playersList) {
- this.updatePlayer(serverPlayer);
+ updated |= this.updatePlayerMulti(serverPlayer);
}
+ if (updated) seenByUpdated();
}
+ // Leaf end - Multithreaded tracker
}
}
diff --git a/net/minecraft/server/level/ServerBossEvent.java b/net/minecraft/server/level/ServerBossEvent.java
index f106373ef3ac4a8685c2939c9e8361688a285913..cb516acac856f30ad0e2d24648bbfd86686339d2 100644
--- a/net/minecraft/server/level/ServerBossEvent.java
+++ b/net/minecraft/server/level/ServerBossEvent.java
@@ -105,6 +105,20 @@ public class ServerBossEvent extends BossEvent {
}
}
+ // Leaf start - Multithreaded tracker
+ public void leaf$addPlayer(org.dreeam.leaf.async.tracker.TrackerCtx ctx, ServerPlayer player) {
+ if (this.players.add(player) && this.visible) {
+ ctx.send(player.connection, ClientboundBossEventPacket.createAddPacket(this));
+ }
+ }
+
+ public void leaf$removePlayer(org.dreeam.leaf.async.tracker.TrackerCtx ctx, ServerPlayer player) {
+ if (this.players.remove(player) && this.visible) {
+ ctx.send(player.connection, ClientboundBossEventPacket.createRemovePacket(this.getId()));
+ }
+ }
+ // Leaf end - Multithreaded tracker
+
public void removeAllPlayers() {
if (!this.players.isEmpty()) {
for (ServerPlayer serverPlayer : Lists.newArrayList(this.players)) {
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index 85c811666b5cc4a01ccb97cef0cf350570bc12c6..153e442bc8b459067be0909080d2aac8bd09a5f3 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -56,12 +56,12 @@ public class ServerEntity {
private static final double TOLERANCE_LEVEL_POSITION = 7.6293945E-6F;
public static final int FORCED_POS_UPDATE_PERIOD = 60;
private static final int FORCED_TELEPORT_PERIOD = 400;
- private final ServerLevel level;
- private final Entity entity;
+ private final ServerLevel level; // Leaf - Multithreaded tracker - diff on change
+ public final Entity entity; // Leaf - Multithreaded tracker - private -> public
private final int updateInterval;
private final boolean trackDelta;
private final Consumer<Packet<?>> broadcast;
- private final BiConsumer<Packet<?>, List<UUID>> broadcastWithIgnore;
+ private final BiConsumer<Packet<?>, List<UUID>> broadcastWithIgnore; // Leaf - Multithreaded tracker - diff on change
private final VecDeltaCodec positionCodec = new VecDeltaCodec();
private byte lastSentYRot;
private byte lastSentXRot;
@@ -73,7 +73,7 @@ public class ServerEntity {
private boolean wasRiding;
private boolean wasOnGround;
@Nullable
- private List<SynchedEntityData.DataValue<?>> trackedDataValues;
+ public List<SynchedEntityData.DataValue<?>> trackedDataValues; // Leaf - Multithreaded tracker - private -> public
private final Set<net.minecraft.server.network.ServerPlayerConnection> trackedPlayers; // Paper
public ServerEntity(
@@ -93,7 +93,7 @@ public class ServerEntity {
this.entity = entity;
this.updateInterval = updateInterval;
this.trackDelta = trackDelta;
- this.broadcastWithIgnore = broadcastWithIgnore;
+ this.broadcastWithIgnore = broadcastWithIgnore; // Leaf - Multithreaded tracker - diff on change
this.positionCodec.setBase(entity.trackingPosition());
this.lastSentMovement = entity.getDeltaMovement();
this.lastSentYRot = Mth.packDegrees(entity.getYRot());
@@ -104,206 +104,371 @@ public class ServerEntity {
}
// Paper start - fix desync when a player is added to the tracker
- private boolean forceStateResync;
+ private boolean forceStateResync; // Leaf - Multithreaded tracker - diff on change
public void onPlayerAdd() {
this.forceStateResync = true;
}
// Paper end - fix desync when a player is added to the tracker
- public void sendChanges() {
+ public void sendChanges() { // Leaf - Multithreaded tracker - diff on change
// Paper start - optimise collisions
if (((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)this.entity).moonrise$isHardColliding()) {
this.teleportDelay = 9999;
}
+ // Leaf - Multithreaded tracker - diff on change
// Paper end - optimise collisions
- List<Entity> passengers = this.entity.getPassengers();
+ List<Entity> passengers = this.entity.getPassengers(); // Leaf - Multithreaded tracker - diff on change
if (!passengers.equals(this.lastPassengers)) {
// Leaf start - Remove stream in entity mountedOrDismounted changes update
List<UUID> list = new ArrayList<>();
- for (Entity entity : this.lastPassengers) {
+ for (Entity entity : this.lastPassengers) { // Leaf - Multithreaded tracker - diff on change
if (!passengers.contains(entity)) {
list.add(entity.getUUID());
}
- }
+ } // Leaf - Multithreaded tracker - diff on change
for (Entity entity : passengers) {
if (!this.lastPassengers.contains(entity)) {
list.add(entity.getUUID());
- }
+ } // Leaf - Multithreaded tracker - diff on change
}
// Leaf end - Remove stream in entity mountedOrDismounted changes update
this.broadcastWithIgnore.accept(new ClientboundSetPassengersPacket(this.entity), list);
- // Paper start - Allow riding players
+ // Paper start - Allow riding players // Leaf - Multithreaded tracker - diff on change
if (this.entity instanceof ServerPlayer player) {
player.connection.send(new ClientboundSetPassengersPacket(this.entity));
}
// Paper end - Allow riding players
- this.lastPassengers = passengers;
+ this.lastPassengers = passengers; // Leaf - Multithreaded tracker - diff on change
}
- if (!this.trackedPlayers.isEmpty() && this.entity instanceof ItemFrame itemFrame /*&& this.tickCount % 10 == 0*/) { // CraftBukkit - moved tickCount below // Paper - Perf: Only tick item frames if players can see it
+ if (!this.trackedPlayers.isEmpty() && this.entity instanceof ItemFrame itemFrame /*&& this.tickCount % 10 == 0*/) { // CraftBukkit - moved tickCount below // Paper - Perf: Only tick item frames if players can see it // Leaf - Multithreaded tracker - diff on change
ItemStack item = itemFrame.getItem();
if (this.level.paperConfig().maps.itemFrameCursorUpdateInterval > 0 && this.tickCount % this.level.paperConfig().maps.itemFrameCursorUpdateInterval == 0 && item.getItem() instanceof MapItem) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks // Paper - Make item frame map cursor update interval configurable
MapId mapId = itemFrame.cachedMapId; // Paper - Perf: Cache map ids on item frames
- MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level);
+ MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level); // Leaf - Multithreaded tracker - diff on change
if (savedData != null) {
for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers) { // Paper
final ServerPlayer serverPlayer = connection.getPlayer(); // Paper
savedData.tickCarriedBy(serverPlayer, item);
- Packet<?> updatePacket = savedData.getUpdatePacket(mapId, serverPlayer);
+ Packet<?> updatePacket = savedData.getUpdatePacket(mapId, serverPlayer); // Leaf - Multithreaded tracker - diff on change
if (updatePacket != null) {
serverPlayer.connection.send(updatePacket);
}
}
- }
+ } // Leaf - Multithreaded tracker - diff on change
}
- this.sendDirtyEntityData();
+ this.sendDirtyEntityData(); // Leaf - Multithreaded tracker - diff on change
}
- if (this.forceStateResync || this.tickCount % this.updateInterval == 0 || this.entity.hasImpulse || this.entity.getEntityData().isDirty()) { // Paper - fix desync when a player is added to the tracker
+ if (this.forceStateResync || this.tickCount % this.updateInterval == 0 || this.entity.hasImpulse || this.entity.getEntityData().isDirty()) { // Paper - fix desync when a player is added to the tracker // Leaf - Multithreaded tracker - diff on change
byte b = Mth.packDegrees(this.entity.getYRot());
byte b1 = Mth.packDegrees(this.entity.getXRot());
- boolean flag = Math.abs(b - this.lastSentYRot) >= 1 || Math.abs(b1 - this.lastSentXRot) >= 1;
+ boolean flag = Math.abs(b - this.lastSentYRot) >= 1 || Math.abs(b1 - this.lastSentXRot) >= 1; // Leaf - Multithreaded tracker - diff on change
if (this.entity.isPassenger()) {
- if (flag) {
+ if (flag) { // Leaf - Multithreaded tracker - diff on change
this.broadcast.accept(new ClientboundMoveEntityPacket.Rot(this.entity.getId(), b, b1, this.entity.onGround()));
this.lastSentYRot = b;
this.lastSentXRot = b1;
- }
+ } // Leaf - Multithreaded tracker - diff on change
- this.positionCodec.setBase(this.entity.trackingPosition());
+ this.positionCodec.setBase(this.entity.trackingPosition()); // Leaf - Multithreaded tracker - diff on change
this.sendDirtyEntityData();
this.wasRiding = true;
} else if (this.entity instanceof AbstractMinecart abstractMinecart
&& abstractMinecart.getBehavior() instanceof NewMinecartBehavior newMinecartBehavior) {
+ // Leaf - Multithreaded tracker - diff on change
this.handleMinecartPosRot(newMinecartBehavior, b, b1, flag);
} else {
this.teleportDelay++;
- Vec3 vec3 = this.entity.trackingPosition();
+ Vec3 vec3 = this.entity.trackingPosition(); // Leaf - Multithreaded tracker - diff on change
// Paper start - reduce allocation of Vec3D here
Vec3 base = this.positionCodec.base;
double vec3_dx = vec3.x - base.x;
double vec3_dy = vec3.y - base.y;
- double vec3_dz = vec3.z - base.z;
+ double vec3_dz = vec3.z - base.z; // Leaf - Multithreaded tracker - diff on change
boolean flag1 = (vec3_dx * vec3_dx + vec3_dy * vec3_dy + vec3_dz * vec3_dz) >= 7.62939453125E-6D;
// Paper end - reduce allocation of Vec3D here
- Packet<?> packet = null;
+ Packet<?> packet = null; // Leaf - Multithreaded tracker - diff on change
boolean flag2 = flag1 || this.tickCount % 60 == 0;
boolean flag3 = false;
boolean flag4 = false;
long l = this.positionCodec.encodeX(vec3);
long l1 = this.positionCodec.encodeY(vec3);
- long l2 = this.positionCodec.encodeZ(vec3);
+ long l2 = this.positionCodec.encodeZ(vec3); // Leaf - Multithreaded tracker - diff on change
boolean flag5 = l < -32768L || l > 32767L || l1 < -32768L || l1 > 32767L || l2 < -32768L || l2 > 32767L;
boolean onGroundChanged = this.wasOnGround != this.entity.onGround(); // Purpur - Dont send useless entity packets
if (this.forceStateResync || this.entity.getRequiresPrecisePosition() // Paper - fix desync when a player is added to the tracker
|| flag5
|| this.teleportDelay > 400
|| this.wasRiding
- || onGroundChanged) { // Purpur - Dont send useless entity packets
+ || onGroundChanged) { // Purpur - Dont send useless entity packets // Leaf - Multithreaded tracker - diff on change
this.wasOnGround = this.entity.onGround();
this.teleportDelay = 0;
packet = ClientboundEntityPositionSyncPacket.of(this.entity);
flag3 = true;
- flag4 = true;
+ flag4 = true; // Leaf - Multithreaded tracker - diff on change
// Gale start - Airplane - better checking for useless move packets
- } else {
+ } else { // Leaf - Multithreaded tracker - diff on change
if (flag2 || flag || this.entity instanceof AbstractArrow) {
- if ((!flag2 || !flag) && !(this.entity instanceof AbstractArrow)) {
+ if ((!flag2 || !flag) && !(this.entity instanceof AbstractArrow)) { // Leaf - Multithreaded tracker - diff on change
if (flag2) {
packet = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) l, (short) l1, (short) l2, this.entity.onGround());
flag3 = true;
- } else if (flag) {
+ } else if (flag) { // Leaf - Multithreaded tracker - diff on change
packet = new ClientboundMoveEntityPacket.Rot(this.entity.getId(), b, b1, this.entity.onGround());
flag4 = true;
}
- } else {
+ } else { // Leaf - Multithreaded tracker - diff on change
packet = new ClientboundMoveEntityPacket.PosRot(this.entity.getId(), (short) l, (short) l1, (short) l2, b, b1, this.entity.onGround());
flag3 = true;
flag4 = true;
}
}
- }
+ } // Leaf - Multithreaded tracker - diff on change
// Gale end - Airplane - better checking for useless move packets
if (org.dreeam.leaf.config.modules.opt.ReduceUselessPackets.reduceUselessEntityMovePackets && !onGroundChanged && isUselessMoveEntityPacket(packet)) packet = null; // Purpur - Dont send useless entity packets
- if (this.entity.hasImpulse || this.trackDelta || this.entity instanceof LivingEntity && ((LivingEntity)this.entity).isFallFlying()) {
+ if (this.entity.hasImpulse || this.trackDelta || this.entity instanceof LivingEntity && ((LivingEntity)this.entity).isFallFlying()) { // Leaf - Multithreaded tracker - diff on change
Vec3 deltaMovement = this.entity.getDeltaMovement();
if (deltaMovement != this.lastSentMovement) { // SparklyPaper start - skip distanceToSqr call in ServerEntity#sendChanges if the delta movement hasn't changed
double d = deltaMovement.distanceToSqr(this.lastSentMovement);
if (d > 1.0E-7 || d > 0.0 && deltaMovement.lengthSqr() == 0.0) {
- this.lastSentMovement = deltaMovement;
- if (this.entity instanceof AbstractHurtingProjectile abstractHurtingProjectile) {
+ this.lastSentMovement = deltaMovement; // Leaf - Multithreaded tracker - diff on change
+ if (this.entity instanceof AbstractHurtingProjectile abstractHurtingProjectile) { // Leaf - Multithreaded tracker - diff on change
this.broadcast
.accept(
new ClientboundBundlePacket(
- List.of(
+ List.of( // Leaf - Multithreaded tracker - diff on change
new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement),
new ClientboundProjectilePowerPacket(abstractHurtingProjectile.getId(), abstractHurtingProjectile.accelerationPower)
)
)
);
- } else {
+ } else { // Leaf - Multithreaded tracker - diff on change
this.broadcast.accept(new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement));
}
}
} // SparklyPaper end
}
- if (packet != null) {
+ if (packet != null) { // Leaf - Multithreaded tracker - diff on change
this.broadcast.accept(packet);
}
- this.sendDirtyEntityData();
+ this.sendDirtyEntityData(); // Leaf - Multithreaded tracker - diff on change
if (flag3) {
this.positionCodec.setBase(vec3);
}
- if (flag4) {
+ if (flag4) { // Leaf - Multithreaded tracker - diff on change
this.lastSentYRot = b;
this.lastSentXRot = b1;
}
- this.wasRiding = false;
+ this.wasRiding = false; // Leaf - Multithreaded tracker - diff on change
}
- byte b2 = Mth.packDegrees(this.entity.getYHeadRot());
+ byte b2 = Mth.packDegrees(this.entity.getYHeadRot()); // Leaf - Multithreaded tracker - diff on change
if (Math.abs(b2 - this.lastSentYHeadRot) >= 1) {
this.broadcast.accept(new ClientboundRotateHeadPacket(this.entity, b2));
this.lastSentYHeadRot = b2;
}
- this.entity.hasImpulse = false;
+ this.entity.hasImpulse = false; // Leaf - Multithreaded tracker - diff on change
this.forceStateResync = false; // Paper - fix desync when a player is added to the tracker
}
- this.tickCount++;
+
+ this.tickCount++; // Leaf - Multithreaded tracker - diff on change
if (this.entity.hurtMarked) {
// CraftBukkit start - Create PlayerVelocity event
boolean cancelled = false;
- if (this.entity instanceof ServerPlayer) {
+ if (this.entity instanceof ServerPlayer) { // Leaf - Multithreaded tracker - diff on change
org.bukkit.entity.Player player = (org.bukkit.entity.Player) this.entity.getBukkitEntity();
org.bukkit.util.Vector velocity = player.getVelocity();
- org.bukkit.event.player.PlayerVelocityEvent event = new org.bukkit.event.player.PlayerVelocityEvent(player, velocity.clone());
+ org.bukkit.event.player.PlayerVelocityEvent event = new org.bukkit.event.player.PlayerVelocityEvent(player, velocity.clone()); // Leaf - Multithreaded tracker - diff on change
if (!event.callEvent()) {
- cancelled = true;
+ cancelled = true; // Leaf - Multithreaded tracker - diff on change
} else if (!velocity.equals(event.getVelocity())) {
player.setVelocity(event.getVelocity());
}
- }
+ } // Leaf - Multithreaded tracker - diff on change
if (cancelled) {
return;
- }
+ } // Leaf - Multithreaded tracker - diff on change
// CraftBukkit end
this.entity.hurtMarked = false;
- this.broadcastAndSend(new ClientboundSetEntityMotionPacket(this.entity));
+ this.broadcastAndSend(new ClientboundSetEntityMotionPacket(this.entity)); // Leaf - Multithreaded tracker - diff on change
}
}
+ // Leaf start - Multithreaded tracker
+ public void leaf$sendChanges(org.dreeam.leaf.async.tracker.TrackerCtx ctx, ChunkMap.TrackedEntity tracker, boolean force) {
+ if (this.forceStateResync && !force) {
+ this.forceStateResync = false;
+ ctx.forceResync(tracker);
+ return;
+ }
+ // Paper start - optimise collisions
+ if (this.entity.moonrise$isHardColliding()) {
+ this.teleportDelay = 9999;
+ }
+ // Paper end - optimise collisions
+ List<Entity> passengers = this.entity.getPassengers();
+ if (!passengers.equals(this.lastPassengers)) {
+ // Leaf start - Remove stream in entity mountedOrDismounted changes update
+ List<UUID> list = new ArrayList<>();
+ for (Entity entity : this.lastPassengers) {
+ if (!passengers.contains(entity)) {
+ list.add(entity.getUUID());
+ }
+ }
+ for (Entity entity : passengers) {
+ if (!this.lastPassengers.contains(entity)) {
+ list.add(entity.getUUID());
+ }
+ }
+ // Leaf end - Remove stream in entity mountedOrDismounted changes update
+ ctx.broadcastIgnorePlayers(tracker, new ClientboundSetPassengersPacket(this.entity), list);
+ // Paper start - Allow riding players
+ if (this.entity instanceof ServerPlayer player) {
+ ctx.send(player.connection, new ClientboundSetPassengersPacket(this.entity));
+ }
+ // Paper end - Allow riding players
+ this.lastPassengers = passengers;
+ }
+ if (!tracker.seenBy.isEmpty() && this.entity instanceof ItemFrame itemFrame /*&& this.tickCount % 10 == 0*/) { // CraftBukkit - moved tickCount below // Paper - Perf: Only tick item frames if players can see it
+ if (this.level.paperConfig().maps.itemFrameCursorUpdateInterval > 0 && this.tickCount % this.level.paperConfig().maps.itemFrameCursorUpdateInterval == 0 && itemFrame.getItem().getItem() instanceof MapItem) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks // Paper - Make item frame map cursor update interval configurable
+ ctx.updateItemFrame(itemFrame);
+ }
+ ctx.sendDirtyEntityData(tracker);
+ }
+
+ if (force || this.tickCount % this.updateInterval == 0 || this.entity.hasImpulse || this.entity.getEntityData().isDirty()) { // Paper - fix desync when a player is added to the tracker
+ byte b = Mth.packDegrees(this.entity.getYRot());
+ byte b1 = Mth.packDegrees(this.entity.getXRot());
+ boolean flag = Math.abs(b - this.lastSentYRot) >= 1 || Math.abs(b1 - this.lastSentXRot) >= 1;
+ if (this.entity.isPassenger()) {
+ if (flag) {
+ ctx.broadcast(tracker, new ClientboundMoveEntityPacket.Rot(this.entity.getId(), b, b1, this.entity.onGround()));
+ this.lastSentYRot = b;
+ this.lastSentXRot = b1;
+ }
+
+ this.positionCodec.setBase(this.entity.trackingPosition());
+ ctx.sendDirtyEntityData(tracker);
+ this.wasRiding = true;
+ } else if (this.entity instanceof AbstractMinecart abstractMinecart
+ && abstractMinecart.getBehavior() instanceof NewMinecartBehavior newMinecartBehavior) {
+ this.leafHandleMinecartPosRot(ctx, tracker, newMinecartBehavior, b, b1, flag);
+ } else {
+ this.teleportDelay++;
+ Vec3 vec3 = this.entity.trackingPosition();
+ // Paper start - reduce allocation of Vec3D here
+ Vec3 base = this.positionCodec.base;
+ double vec3_dx = vec3.x - base.x;
+ double vec3_dy = vec3.y - base.y;
+ double vec3_dz = vec3.z - base.z;
+ boolean flag1 = (vec3_dx * vec3_dx + vec3_dy * vec3_dy + vec3_dz * vec3_dz) >= 7.62939453125E-6D;
+ // Paper end - reduce allocation of Vec3D here
+ Packet<? super ClientGamePacketListener> packet = null;
+ boolean flag2 = flag1 || this.tickCount % 60 == 0;
+ boolean flag3 = false;
+ boolean flag4 = false;
+ long l = this.positionCodec.encodeX(vec3);
+ long l1 = this.positionCodec.encodeY(vec3);
+ long l2 = this.positionCodec.encodeZ(vec3);
+ boolean flag5 = l < -32768L || l > 32767L || l1 < -32768L || l1 > 32767L || l2 < -32768L || l2 > 32767L;
+ boolean onGroundChanged = this.wasOnGround != this.entity.onGround();
+ if (force || this.entity.getRequiresPrecisePosition() // Paper - fix desync when a player is added to the tracker
+ || flag5
+ || this.teleportDelay > 400
+ || this.wasRiding
+ || onGroundChanged) {
+ this.wasOnGround = this.entity.onGround();
+ this.teleportDelay = 0;
+ packet = ClientboundEntityPositionSyncPacket.of(this.entity);
+ flag3 = true;
+ flag4 = true;
+ // Gale start - Airplane - better checking for useless move packets
+ } else {
+ if (flag2 || flag || this.entity instanceof AbstractArrow) {
+ if ((!flag2 || !flag) && !(this.entity instanceof AbstractArrow)) {
+ if (flag2) {
+ packet = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) l, (short) l1, (short) l2, this.entity.onGround());
+ flag3 = true;
+ } else if (flag) {
+ packet = new ClientboundMoveEntityPacket.Rot(this.entity.getId(), b, b1, this.entity.onGround());
+ flag4 = true;
+ }
+ } else {
+ packet = new ClientboundMoveEntityPacket.PosRot(this.entity.getId(), (short) l, (short) l1, (short) l2, b, b1, this.entity.onGround());
+ flag3 = true;
+ flag4 = true;
+ }
+ }
+ }
+ // Gale end - Airplane - better checking for useless move packets
+
+ if (org.dreeam.leaf.config.modules.opt.ReduceUselessPackets.reduceUselessEntityMovePackets && !onGroundChanged && isUselessMoveEntityPacket(packet)) packet = null; // Purpur - Dont send useless entity packets
+
+ if (this.entity.hasImpulse || this.trackDelta || this.entity instanceof LivingEntity && ((LivingEntity)this.entity).isFallFlying()) {
+ Vec3 deltaMovement = this.entity.getDeltaMovement();
+ if (deltaMovement != this.lastSentMovement) { // SparklyPaper start - skip distanceToSqr call in ServerEntity#sendChanges if the delta movement hasn't changed
+ double d = deltaMovement.distanceToSqr(this.lastSentMovement);
+ if (d > 1.0E-7 || d > 0.0 && deltaMovement.lengthSqr() == 0.0) {
+ this.lastSentMovement = deltaMovement;
+ if (this.entity instanceof AbstractHurtingProjectile abstractHurtingProjectile) {
+ ctx.broadcast(tracker, new ClientboundBundlePacket(List.of(
+ new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement),
+ new ClientboundProjectilePowerPacket(abstractHurtingProjectile.getId(), abstractHurtingProjectile.accelerationPower)
+ )));
+ } else {
+ ctx.broadcast(tracker, new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement));
+ }
+ }
+ } // SparklyPaper end
+ }
+
+ if (packet != null) {
+ ctx.broadcast(tracker, packet);
+ }
+
+ ctx.sendDirtyEntityData(tracker);
+ if (flag3) {
+ this.positionCodec.setBase(vec3);
+ }
+
+ if (flag4) {
+ this.lastSentYRot = b;
+ this.lastSentXRot = b1;
+ }
+
+ this.wasRiding = false;
+ }
+
+ byte b2 = Mth.packDegrees(this.entity.getYHeadRot());
+ if (Math.abs(b2 - this.lastSentYHeadRot) >= 1) {
+ ctx.broadcast(tracker, new ClientboundRotateHeadPacket(this.entity, b2));
+ this.lastSentYHeadRot = b2;
+ }
+
+ this.entity.hasImpulse = false;
+ }
+
+ this.tickCount++;
+ if (this.entity.hurtMarked && !(this.entity instanceof ServerPlayer)) {
+ this.entity.hurtMarked = false;
+ ctx.broadcastAndSend(tracker, new ClientboundSetEntityMotionPacket(this.entity));
+ }
+ }
+ // Leaf end - Multithreaded tracker
+
private Stream<Entity> mountedOrDismounted(List<Entity> entities) {
return Streams.concat(
this.lastPassengers.stream().filter(entity -> !entities.contains(entity)),
@@ -357,7 +522,36 @@ public class ServerEntity {
this.positionCodec.setBase(this.entity.position());
}
+ // Leaf start - Multithreaded tracker
+ private void leafHandleMinecartPosRot(org.dreeam.leaf.async.tracker.TrackerCtx ctx, ChunkMap.TrackedEntity tracker, NewMinecartBehavior behavior, byte yRot, byte xRot, boolean dirty) {
+ ctx.sendDirtyEntityData(tracker);
+ if (behavior.lerpSteps.isEmpty()) {
+ Vec3 deltaMovement = this.entity.getDeltaMovement();
+ double d = deltaMovement.distanceToSqr(this.lastSentMovement);
+ Vec3 vec3 = this.entity.trackingPosition();
+ boolean flag = this.positionCodec.delta(vec3).lengthSqr() >= 7.6293945E-6F;
+ boolean flag1 = flag || this.tickCount % 60 == 0;
+ if (flag1 || dirty || d > 1.0E-7) {
+ ctx.broadcast(tracker, new ClientboundMoveMinecartPacket(
+ this.entity.getId(),
+ List.of(
+ new NewMinecartBehavior.MinecartStep(
+ this.entity.position(), this.entity.getDeltaMovement(), this.entity.getYRot(), this.entity.getXRot(), 1.0F
+ ))));
+ }
+ } else {
+ ctx.broadcast(tracker, new ClientboundMoveMinecartPacket(this.entity.getId(), List.copyOf(behavior.lerpSteps)));
+ behavior.lerpSteps.clear();
+ }
+
+ this.lastSentYRot = yRot;
+ this.lastSentXRot = xRot;
+ this.positionCodec.setBase(this.entity.position());
+ }
+ // Leaf end - Multithreaded tracker
+
public void removePairing(ServerPlayer player) {
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { ((ServerLevel) this.entity.level()).leaf$asyncTracker.ctx().stopSeenByPlayer(player.connection, this.entity); return; } // Leaf - Multithreaded tracker
this.entity.stopSeenByPlayer(player);
player.connection.send(new ClientboundRemoveEntitiesPacket(this.entity.getId()));
}
@@ -424,6 +618,56 @@ public class ServerEntity {
}
}
+ // Leaf start - Multithreaded tracker
+ public boolean leaf$sendPairingData(List<Packet<? super ClientGamePacketListener>> consumer) {
+ if (this.entity.isRemoved()) {
+ // CraftBukkit start - Remove useless error spam, just return
+ // LOGGER.warn("Fetching packet for removed entity {}", this.entity);
+ return false;
+ // CraftBukkit end
+ }
+
+ if (this.trackedDataValues != null) {
+ consumer.add(new ClientboundSetEntityDataPacket(this.entity.getId(), this.trackedDataValues));
+ }
+
+ boolean flag = false;
+ if (this.entity instanceof LivingEntity livingEntity) {
+ List<ClientboundUpdateAttributesPacket.AttributeSnapshot> syncableAttributes = livingEntity.getAttributes().getSyncableAttributesPacket();
+ if (!syncableAttributes.isEmpty()) {
+ flag = livingEntity instanceof ServerPlayer && livingEntity.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH) != null;
+ consumer.add(new ClientboundUpdateAttributesPacket(this.entity.getId(), syncableAttributes));
+ }
+ if (livingEntity.hasItemInSlots()) {
+ List<Pair<EquipmentSlot, ItemStack>> list = Lists.newArrayList();
+ for (EquipmentSlot equipmentSlot : EquipmentSlot.VALUES_ARRAY) { // Gale - JettPack - reduce array allocations
+ ItemStack itemBySlot = livingEntity.getItemBySlot(equipmentSlot);
+ if (!itemBySlot.isEmpty()) {
+ list.add(Pair.of(equipmentSlot, itemBySlot.copy()));
+ }
+ }
+ if (!list.isEmpty()) {
+ consumer.add(new ClientboundSetEquipmentPacket(this.entity.getId(), list, true)); // Paper - data sanitization
+ }
+ }
+ }
+
+ if (!this.entity.getPassengers().isEmpty()) {
+ consumer.add(new ClientboundSetPassengersPacket(this.entity));
+ }
+
+ if (this.entity.isPassenger()) {
+ consumer.add(new ClientboundSetPassengersPacket(this.entity.getVehicle()));
+ }
+
+ if (this.entity instanceof Leashable leashable && leashable.isLeashed()) {
+ consumer.add(new ClientboundSetEntityLinkPacket(this.entity, leashable.getLeashHolder()));
+ }
+
+ return flag;
+ }
+ // Leaf end - Multithreaded tracker
+
public Vec3 getPositionBase() {
return this.positionCodec.getBase();
}
@@ -444,6 +688,7 @@ public class ServerEntity {
return Mth.unpackDegrees(this.lastSentYHeadRot);
}
+ // Leaf - Multithreaded tracker - diff on change
private void sendDirtyEntityData() {
SynchedEntityData entityData = this.entity.getEntityData();
List<SynchedEntityData.DataValue<?>> list = entityData.packDirty();
@@ -451,10 +696,12 @@ public class ServerEntity {
this.trackedDataValues = entityData.getNonDefaultValues();
this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list));
}
+ // Leaf - Multithreaded tracker - diff on change
if (this.entity instanceof LivingEntity) {
Set<AttributeInstance> attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync();
if (!attributesToSync.isEmpty()) {
+ // Leaf - Multithreaded tracker - diff on change
// CraftBukkit start - Send scaled max health
if (this.entity instanceof ServerPlayer serverPlayer) {
serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false);
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 3232c025b2412f293c266e5f9e643f16f8693ef3..f58b91b277ba85fa7c0e7ad10157ecbf10023065 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -221,6 +221,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current)
public boolean hasRidableMoveEvent = false; // Purpur - Ridables
final List<ServerPlayer> realPlayers; // Leaves - skip
+ public final org.dreeam.leaf.async.tracker.AsyncTracker leaf$asyncTracker = new org.dreeam.leaf.async.tracker.AsyncTracker(this); // Leaf - Multithreaded tracker
@Override
public @Nullable LevelChunk getChunkIfLoaded(int x, int z) {
@@ -987,6 +988,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.tickBlockEntities();
}
// Paper - rewrite chunk system
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { this.leaf$asyncTracker.onEntitiesTickEnd(); } // Leaf - Multithreaded tracker
}
@Override
diff --git a/net/minecraft/world/entity/EntityEquipment.java b/net/minecraft/world/entity/EntityEquipment.java
index 94c7ae9535a235abb8fddf0ca6578dfae2e675bb..6db2eaa613599e6af5fea0922d2876b8e7926837 100644
--- a/net/minecraft/world/entity/EntityEquipment.java
+++ b/net/minecraft/world/entity/EntityEquipment.java
@@ -46,11 +46,14 @@ public class EntityEquipment implements net.caffeinemc.mods.lithium.common.entit
}
public boolean isEmpty() {
- for (ItemStack itemStack : this.items.values()) {
- if (!itemStack.isEmpty()) {
+ // Leaf start - Multithreaded tracker
+ for (int i = 0; i < EquipmentSlot.VALUES_ARRAY.length; i++) {
+ ItemStack itemStack = this.items.get(EquipmentSlot.VALUES_ARRAY[i]);
+ if (itemStack != null && !itemStack.isEmpty()) {
return false;
}
}
+ // Leaf end - Multithreaded tracker
return true;
}
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index a2102e3739b19b99da81cc7a99ed2c8f01c46927..77ddcb214003e1fd2c847aea033217e542bc15fb 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -2939,6 +2939,12 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
return this.equipment.get(slot);
}
+ // Leaf start - Multithreaded tracker
+ public boolean hasItemInSlots() {
+ return !this.equipment.isEmpty();
+ }
+ // Leaf end - Multithreaded tracker
+
public void setItemSlot(EquipmentSlot slot, ItemStack stack) {
// Paper start
this.setItemSlot(slot, stack, false);
diff --git a/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java b/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
index 325ec57df2885f5e81b8a6b61e3a9fed9484b30f..abc5c097861d0decf49d0d3970ab48f1cf8b1cf1 100644
--- a/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
+++ b/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
@@ -35,13 +35,20 @@ public class NewMinecartBehavior extends MinecartBehavior {
private int cachedLerpDelay;
private float cachedPartialTick;
private int lerpDelay = 0;
- public final List<NewMinecartBehavior.MinecartStep> lerpSteps = new LinkedList<>();
+ public final List<NewMinecartBehavior.MinecartStep> lerpSteps; // Leaf - Multithreaded tracker
public final List<NewMinecartBehavior.MinecartStep> currentLerpSteps = new LinkedList<>();
public double currentLerpStepsTotalWeight = 0.0;
public NewMinecartBehavior.MinecartStep oldLerp = NewMinecartBehavior.MinecartStep.ZERO;
public NewMinecartBehavior(AbstractMinecart minecart) {
super(minecart);
+ // Leaf start - Multithreaded tracker
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
+ lerpSteps = it.unimi.dsi.fastutil.objects.ObjectLists.synchronize(new it.unimi.dsi.fastutil.objects.ObjectArrayList<>());
+ } else {
+ lerpSteps = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
+ }
+ // Leaf end - Multithreaded tracker
}
@Override