diff --git a/leaf-server/minecraft-patches/features/0169-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0169-Multithreaded-Tracker.patch index 1a22dea5..cb166792 100644 --- a/leaf-server/minecraft-patches/features/0169-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0169-Multithreaded-Tracker.patch @@ -604,7 +604,7 @@ index f106373ef3ac4a8685c2939c9e8361688a285913..7df467924d029d6e42b1d0b039cb9272 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 add696ec1835eb161d6fc94509a2a77febd23d69..ca948c0f4f48b567b66e4b992eef99d3a9210d45 100644 +index add696ec1835eb161d6fc94509a2a77febd23d69..23a449cec699c0a32d6fffc0f88e84895b162856 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java @@ -57,11 +57,13 @@ public class ServerEntity { @@ -629,7 +629,7 @@ index add696ec1835eb161d6fc94509a2a77febd23d69..ca948c0f4f48b567b66e4b992eef99d3 this.positionCodec.setBase(entity.trackingPosition()); this.lastSentMovement = entity.getDeltaMovement(); this.lastSentYRot = Mth.packDegrees(entity.getYRot()); -@@ -105,204 +108,413 @@ public class ServerEntity { +@@ -105,204 +108,415 @@ public class ServerEntity { // Paper start - fix desync when a player is added to the tracker private boolean forceStateResync; @@ -992,8 +992,10 @@ index add696ec1835eb161d6fc94509a2a77febd23d69..ca948c0f4f48b567b66e4b992eef99d3 + if (d > 1.0E-7 || d > 0.0 && deltaMovement.lengthSqr() == 0.0) { + this.lastSentMovement = deltaMovement; + if (this.entity instanceof AbstractHurtingProjectile abstractHurtingProjectile) { -+ trackedEntity.leafBroadcast(ctx, new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement)); -+ trackedEntity.leafBroadcast(ctx, new ClientboundProjectilePowerPacket(abstractHurtingProjectile.getId(), abstractHurtingProjectile.accelerationPower)); ++ trackedEntity.leafBroadcast(ctx, new ClientboundBundlePacket(List.of( ++ new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement), ++ new ClientboundProjectilePowerPacket(abstractHurtingProjectile.getId(), abstractHurtingProjectile.accelerationPower) ++ ))); + } else { + trackedEntity.leafBroadcast(ctx, new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement)); + } @@ -1043,7 +1045,7 @@ index add696ec1835eb161d6fc94509a2a77febd23d69..ca948c0f4f48b567b66e4b992eef99d3 private Stream mountedOrDismounted(List entities) { return Streams.concat( this.lastPassengers.stream().filter(entity -> !entities.contains(entity)), -@@ -356,6 +568,39 @@ public class ServerEntity { +@@ -356,6 +570,39 @@ public class ServerEntity { this.positionCodec.setBase(this.entity.position()); } @@ -1083,14 +1085,16 @@ index add696ec1835eb161d6fc94509a2a77febd23d69..ca948c0f4f48b567b66e4b992eef99d3 public void removePairing(ServerPlayer player) { this.entity.stopSeenByPlayer(player); player.connection.send(new ClientboundRemoveEntitiesPacket(this.entity.getId())); -@@ -368,8 +613,21 @@ public class ServerEntity { +@@ -368,8 +615,23 @@ public class ServerEntity { this.entity.startSeenByPlayer(player); } + // Leaf start - Multithreaded tracker + public void leafAddPairing(org.dreeam.leaf.async.tracker.TrackerCtx ctx, ServerPlayer player) { + final net.minecraft.server.network.ServerGamePacketListenerImpl connection = player.connection; -+ this.sendPairingData0(player, p -> ctx.send(connection, p), true); ++ List> list = new ArrayList<>(); ++ this.sendPairingData0(player, list::add, true); ++ ctx.send(connection, new ClientboundBundlePacket(list)); + ctx.startSeenByPlayer(connection, this.entity); + } + @@ -1105,7 +1109,7 @@ index add696ec1835eb161d6fc94509a2a77febd23d69..ca948c0f4f48b567b66e4b992eef99d3 // CraftBukkit start - Remove useless error spam, just return // LOGGER.warn("Fetching packet for removed entity {}", this.entity); return; -@@ -407,7 +665,7 @@ public class ServerEntity { +@@ -407,7 +669,7 @@ public class ServerEntity { if (!list.isEmpty()) { consumer.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), list, true)); // Paper - data sanitization } @@ -1114,7 +1118,7 @@ index add696ec1835eb161d6fc94509a2a77febd23d69..ca948c0f4f48b567b66e4b992eef99d3 } if (!this.entity.getPassengers().isEmpty()) { -@@ -443,6 +701,7 @@ public class ServerEntity { +@@ -443,6 +705,7 @@ public class ServerEntity { return Mth.unpackDegrees(this.lastSentYHeadRot); } @@ -1122,7 +1126,7 @@ index add696ec1835eb161d6fc94509a2a77febd23d69..ca948c0f4f48b567b66e4b992eef99d3 public void sendDirtyEntityData() { SynchedEntityData entityData = this.entity.getEntityData(); List> list = entityData.packDirty(); -@@ -450,10 +709,12 @@ public class ServerEntity { +@@ -450,10 +713,12 @@ public class ServerEntity { this.trackedDataValues = entityData.getNonDefaultValues(); this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list)); } @@ -1135,7 +1139,7 @@ index add696ec1835eb161d6fc94509a2a77febd23d69..ca948c0f4f48b567b66e4b992eef99d3 // CraftBukkit start - Send scaled max health if (this.entity instanceof ServerPlayer serverPlayer) { serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); -@@ -462,11 +723,38 @@ public class ServerEntity { +@@ -462,11 +727,38 @@ public class ServerEntity { this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); } @@ -1214,7 +1218,7 @@ index 08d12a1acc3a672a77daa15f82392cd603c30283..b0ac6de9e0f15d234781fc43dd6fd1d5 } } diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index f98617c5d55876ac91377f3014d6f0438305d7b3..25871b2db4888d8729f2dd64fdac16dc6eecc5e9 100644 +index 070fc3b4ec0558c597eb411db5b22edc447fe976..512d9b22e79c2bff18defa2f101475d315098176 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -1928,7 +1928,7 @@ public class ServerGamePacketListenerImpl diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/AsyncTracker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/AsyncTracker.java index 554a6758..b70bebfe 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/AsyncTracker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/AsyncTracker.java @@ -62,26 +62,26 @@ public final class AsyncTracker { return; } } - handle(world, task); + handle(world, task, false); } public static void onTickEnd(MinecraftServer server) { for (ServerLevel world : server.getAllLevels()) { Future[] task = world.trackerTask; if (task != null) { - handle(world, task); + handle(world, task, false); } } } - private static void handle(ServerLevel world, Future[] futures) { + private static void handle(ServerLevel world, Future[] futures, boolean flush) { try { TrackerCtx ctx = futures[0].get(); for (int i = 1; i < futures.length; i++) { ctx.join(futures[i].get()); } world.trackerTask = null; - ctx.handle(); + ctx.handle(flush); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (ExecutionException e) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/TrackerCtx.java b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/TrackerCtx.java index 16348b7e..958b2d96 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/TrackerCtx.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/TrackerCtx.java @@ -5,9 +5,9 @@ import io.papermc.paper.event.player.PlayerTrackEntityEvent; import io.papermc.paper.event.player.PlayerUntrackEntityEvent; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; -import net.minecraft.network.protocol.game.ClientboundBundlePacket; import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket; import net.minecraft.server.level.ChunkMap; @@ -25,12 +25,8 @@ import net.minecraft.world.level.saveddata.maps.MapId; import net.minecraft.world.level.saveddata.maps.MapItemSavedData; import org.bukkit.event.player.PlayerVelocityEvent; -import java.util.Arrays; - public final class TrackerCtx { - private static final int SIZE_LIMIT_PER_BUNDLE = 4096; - - private final Reference2ReferenceOpenHashMap>> packets; + private final Reference2ReferenceOpenHashMap>> packets; private final ServerLevel world; private final ObjectArrayList bukkitVelocityEvent = new ObjectArrayList<>(); private final ObjectArrayList bukkitItemFrames = new ObjectArrayList<>(); @@ -108,7 +104,7 @@ public final class TrackerCtx { } public void send(ServerPlayerConnection connection, Packet packet) { - packets.computeIfAbsent(connection, x -> new ObjectArrayList<>()).add(packet); + packets.computeIfAbsent(connection, x -> ReferenceArrayList.wrap(new Packet[16])).add(packet); } void join(TrackerCtx other) { @@ -123,12 +119,12 @@ public final class TrackerCtx { var iterator = other.packets.reference2ReferenceEntrySet().fastIterator(); while (iterator.hasNext()) { var entry = iterator.next(); - packets.computeIfAbsent(entry.getKey(), x -> new ObjectArrayList<>()).addAll(entry.getValue()); + packets.computeIfAbsent(entry.getKey(), x -> ReferenceArrayList.wrap(new Packet[0])).addAll(entry.getValue()); } } - void handle() { - handlePackets(world, packets); + void handle(boolean flush) { + handlePackets(world, packets, flush); if (!pluginEntity.isEmpty()) { for (final Entity entity : pluginEntity) { @@ -237,10 +233,10 @@ public final class TrackerCtx { paperStopSeen.clear(); } - handlePackets(world, packets); + handlePackets(world, packets, flush); } - private static void handlePackets(ServerLevel world, Reference2ReferenceOpenHashMap>> packets) { + private static void handlePackets(ServerLevel world, Reference2ReferenceOpenHashMap>> packets, boolean flush) { if (packets.isEmpty()) { return; } @@ -248,25 +244,16 @@ public final class TrackerCtx { while (iter.hasNext()) { var entry = iter.next(); ServerPlayerConnection connection = entry.getKey(); - ObjectArrayList> list = entry.getValue(); + ReferenceArrayList> list = entry.getValue(); if (!world.equals(connection.getPlayer().level())) { continue; } - int size = list.size(); - if (size > SIZE_LIMIT_PER_BUNDLE) { - int from = 0; - while (from < size) { - int chunkLen = Math.min(SIZE_LIMIT_PER_BUNDLE, size - from); - Packet[] chunk = new Packet[chunkLen]; - list.getElements(from, chunk, 0, chunkLen); - connection.send(new ClientboundBundlePacket(Arrays.asList(chunk))); - from += chunkLen; - } - } else { - connection.send(new ClientboundBundlePacket(list)); + Packet[] packetsRaw = list.elements(); + for (int i = 0, size = list.size(); i < size; i++) { + connection.send(packetsRaw[i]); } - if (connection instanceof ServerGamePacketListenerImpl conn) { - conn.connection.flushChannel(); + if (flush && connection instanceof ServerGamePacketListenerImpl playerConnection) { + playerConnection.connection.flushChannel(); } } packets.clear();