mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-26 02:19:19 +00:00
Originally vanilla logic is to use stream, and Mojang switched it to Guava's Collections2 since 1.21.4. It is much faster than using stream or manually adding to a new ArrayList. Manually adding to a new ArrayList requires allocating a new object array. However, the Collections2 lazy handles filter condition on iteration, so much better.
1478 lines
80 KiB
Diff
1478 lines
80 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 1b8193587814225c2ef2c5d9e667436eb50ff6c5..2a2626e90836ae52a8a686b2040843c6644b6914 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) {
|
|
@@ -164,6 +173,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 +188,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 +202,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 +226,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 bdc1200ef5317fdaf58973bf580b0a672aee800f..20b1186f53c267f69ed7852f5cf3dd2460f8200d 100644
|
|
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
|
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
|
@@ -344,7 +344,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.ConcurrentLongHashSet() : new LongOpenHashSet(); // Leaf - Multithreaded tracker
|
|
|
|
private static final byte CHUNK_TICKET_STAGE_NONE = 0;
|
|
private static final byte CHUNK_TICKET_STAGE_LOADING = 1;
|
|
@@ -422,6 +422,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 = this.world.moonrise$getNearbyPlayers().getChunk(chunkX, chunkZ);
|
|
+ if (trackedChunk != null) {
|
|
+ trackedChunk.trackingUpdateCountAtomic.getAndIncrement();
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
return;
|
|
}
|
|
throw new IllegalStateException();
|
|
diff --git a/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java b/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java
|
|
index 9c0c99b936b4a82ebfe924866e53ec71f7bbe9ad..2ccff968cb2065d34fad4d27573f9e3081edb2f2 100644
|
|
--- a/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java
|
|
+++ b/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java
|
|
@@ -32,6 +32,7 @@ public class ClientboundUpdateAttributesPacket implements Packet<ClientGamePacke
|
|
this.attributes = Lists.newArrayList();
|
|
|
|
for (AttributeInstance attributeInstance : attributes) {
|
|
+ if (attributeInstance == null) continue; // Leaf - Multithreaded tracker
|
|
this.attributes
|
|
.add(
|
|
new ClientboundUpdateAttributesPacket.AttributeSnapshot(
|
|
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
|
index efb281f71ef5404fa40b7d04104b1c15d14e2c05..90409e9ef5cac2abe6748b723207c33224821194 100644
|
|
--- a/net/minecraft/server/MinecraftServer.java
|
|
+++ b/net/minecraft/server/MinecraftServer.java
|
|
@@ -1732,6 +1732,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
|
|
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { org.dreeam.leaf.async.tracker.AsyncTracker.onTickEnd(this); } // Leaf - Multithreaded tracker
|
|
this.tickConnection();
|
|
this.playerList.tick();
|
|
if (this.tickRateManager.runsNormally()) {
|
|
@@ -1809,6 +1810,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
Map<ResourceKey<Level>, ServerLevel> oldLevels = this.levels;
|
|
Map<ResourceKey<Level>, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels);
|
|
newLevels.remove(level.dimension());
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
|
|
+ level.trackerTask = null;
|
|
+ }
|
|
+ // Leaf end - 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 74d11e8983f12f6f33fe2eb3016730507e1031d4..42555d9fc1b37696b8da4c85f9a809819bcc6dde 100644
|
|
--- a/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/net/minecraft/server/level/ChunkMap.java
|
|
@@ -1013,6 +1013,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;
|
|
+ org.dreeam.leaf.async.tracker.AsyncTracker.tick(level);
|
|
+ return;
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
// Paper start - optimise entity tracker
|
|
if (true) {
|
|
this.newTrackerTick();
|
|
@@ -1135,12 +1142,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) {
|
|
@@ -1150,6 +1205,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;
|
|
@@ -1162,27 +1218,120 @@ 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 void leafTick(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;
|
|
+ if (!this.seenBy.isEmpty()) {
|
|
+ for (final ServerPlayerConnection conn : this.seenBy()) {
|
|
+ if (this.seenBy.remove(conn)) {
|
|
+ ctx.stopSeenByPlayer(conn, this.entity);
|
|
+ ctx.send(conn, new net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket(this.entity.getId()));
|
|
+ }
|
|
+ }
|
|
+ this.seenByUpdated();
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+ final double dx = player.getX() - ex;
|
|
+ final double dz = player.getZ() - ez;
|
|
+ final double dy = player.getY() - 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)) {
|
|
+ this.serverEntity.leafAddPairing(ctx, player);
|
|
+ this.serverEntity.onPlayerAdd();
|
|
+ updated = true;
|
|
+ }
|
|
+ } else if (this.seenBy.remove(player.connection)) {
|
|
+ ctx.stopSeenByPlayer(player.connection, this.entity);
|
|
+ ctx.send(player.connection, new net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket(this.entity.getId()));
|
|
+ updated = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (updated) {
|
|
+ this.seenByUpdated();
|
|
+ }
|
|
+ if (!chunkStateChanged) {
|
|
+ return;
|
|
+ }
|
|
+ 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);
|
|
+ ctx.send(conn, new net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket(this.entity.getId()));
|
|
+ updated = true;
|
|
+ }
|
|
+ }
|
|
+ if (updated) {
|
|
+ this.seenByUpdated();
|
|
+ }
|
|
+ }
|
|
+ // 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;
|
|
@@ -1193,12 +1342,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
|
|
@@ -1208,10 +1358,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
|
|
@@ -1238,20 +1389,46 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
|
|
public void broadcast(Packet<?> packet) {
|
|
- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) {
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
+ 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) {
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) {
|
|
if (!ignoredPlayers.contains(serverPlayerConnection.getPlayer().getUUID())) {
|
|
serverPlayerConnection.send(packet);
|
|
}
|
|
}
|
|
}
|
|
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ public void leafBroadcast(org.dreeam.leaf.async.tracker.TrackerCtx ctx, Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener> packet) {
|
|
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) {
|
|
+ ctx.send(serverPlayerConnection, packet);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void leafBroadcastIgnorePlayers(org.dreeam.leaf.async.tracker.TrackerCtx ctx, Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener> packet, List<UUID> ignoredPlayers) {
|
|
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) {
|
|
+ if (!ignoredPlayers.contains(serverPlayerConnection.getPlayer().getUUID())) {
|
|
+ ctx.send(serverPlayerConnection, packet);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void leafBroadcastAndSend(org.dreeam.leaf.async.tracker.TrackerCtx ctx, Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener> packet) {
|
|
+ leafBroadcast(ctx, packet);
|
|
+ if (this.entity instanceof ServerPlayer serverPlayer) {
|
|
+ ctx.send(serverPlayer.connection, packet);
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
+
|
|
public void broadcastAndSend(Packet<?> packet) {
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
this.broadcast(packet);
|
|
if (this.entity instanceof ServerPlayer) {
|
|
((ServerPlayer)this.entity).connection.send(packet);
|
|
@@ -1259,21 +1436,34 @@ 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
|
|
+ //org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot // Leaf - petal - Multithreaded tracker - We can remove async too
|
|
if (this.seenBy.remove(player.connection)) {
|
|
this.serverEntity.removePairing(player);
|
|
+ this.seenByUpdated(); // Leaf - Multithreaded tracker
|
|
}
|
|
}
|
|
|
|
public void updatePlayer(ServerPlayer player) {
|
|
- org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
|
|
+ //org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot // Leaf - petal - Multithreaded tracker - We can update async
|
|
if (player != this.entity) {
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled && player == null) return; // Leaf - Multithreaded tracker
|
|
// Paper start - remove allocation of Vec3D here
|
|
// Vec3 vec3 = player.position().subtract(this.entity.position());
|
|
double vec3_dx = player.getX() - this.entity.getX();
|
|
@@ -1301,6 +1491,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);
|
|
@@ -1309,11 +1500,60 @@ 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)) {
|
|
+ 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 // Leaf - petal - Multithreaded tracker - We can update async
|
|
+ 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);
|
|
}
|
|
@@ -1339,10 +1579,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..7df467924d029d6e42b1d0b039cb9272bb068c4d 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 leafAddPlayer(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 leafRemovePlayer(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 0dee18df07c979da6125a4e7a955343e44d67ac2..dae07c6c31949c75c810c9260232cccc8160b606 100644
|
|
--- a/net/minecraft/server/level/ServerEntity.java
|
|
+++ b/net/minecraft/server/level/ServerEntity.java
|
|
@@ -57,11 +57,13 @@ public class ServerEntity {
|
|
public static final int FORCED_POS_UPDATE_PERIOD = 60;
|
|
private static final int FORCED_TELEPORT_PERIOD = 400;
|
|
private final ServerLevel level;
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
private final Entity entity;
|
|
private final int updateInterval;
|
|
private final boolean trackDelta;
|
|
private final Consumer<Packet<?>> broadcast;
|
|
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;
|
|
@@ -94,6 +96,7 @@ public class ServerEntity {
|
|
this.updateInterval = updateInterval;
|
|
this.trackDelta = trackDelta;
|
|
this.broadcastWithIgnore = broadcastWithIgnore;
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
this.positionCodec.setBase(entity.trackingPosition());
|
|
this.lastSentMovement = entity.getDeltaMovement();
|
|
this.lastSentYRot = Mth.packDegrees(entity.getYRot());
|
|
@@ -105,204 +108,415 @@ public class ServerEntity {
|
|
|
|
// Paper start - fix desync when a player is added to the tracker
|
|
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
|
|
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
public void sendChanges() {
|
|
// 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();
|
|
+ // 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) {
|
|
+ // 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
|
|
+ // 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;
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
}
|
|
|
|
+ // 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
|
|
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);
|
|
+ // 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);
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
if (updatePacket != null) {
|
|
serverPlayer.connection.send(updatePacket);
|
|
}
|
|
}
|
|
}
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
}
|
|
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
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
|
|
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;
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
if (this.entity.isPassenger()) {
|
|
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
|
|
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
this.positionCodec.setBase(this.entity.trackingPosition());
|
|
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();
|
|
+ // 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;
|
|
+ // 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;
|
|
+ // 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);
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
boolean flag5 = l < -32768L || l > 32767L || l1 < -32768L || l1 > 32767L || l2 < -32768L || l2 > 32767L;
|
|
if (this.forceStateResync || this.entity.getRequiresPrecisePosition() // Paper - fix desync when a player is added to the tracker
|
|
|| flag5
|
|
|| this.teleportDelay > 400
|
|
|| this.wasRiding
|
|
|| this.wasOnGround != this.entity.onGround()) {
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
this.wasOnGround = this.entity.onGround();
|
|
this.teleportDelay = 0;
|
|
packet = ClientboundEntityPositionSyncPacket.of(this.entity);
|
|
flag3 = true;
|
|
flag4 = true;
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
// Gale start - Airplane - better checking for useless move packets
|
|
} else {
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
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) {
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
packet = new ClientboundMoveEntityPacket.Rot(this.entity.getId(), b, b1, this.entity.onGround());
|
|
flag4 = true;
|
|
}
|
|
} 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 && isUselessMoveEntityPacket(packet)) packet = null; // Purpur - Dont send useless entity packets
|
|
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
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;
|
|
+ // 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(
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement),
|
|
new ClientboundProjectilePowerPacket(abstractHurtingProjectile.getId(), abstractHurtingProjectile.accelerationPower)
|
|
)
|
|
)
|
|
);
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
} else {
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
this.broadcast.accept(new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement));
|
|
}
|
|
}
|
|
} // SparklyPaper end
|
|
}
|
|
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
if (packet != null) {
|
|
this.broadcast.accept(packet);
|
|
}
|
|
|
|
this.sendDirtyEntityData();
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
if (flag3) {
|
|
this.positionCodec.setBase(vec3);
|
|
}
|
|
|
|
if (flag4) {
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
this.lastSentYRot = b;
|
|
this.lastSentXRot = b1;
|
|
}
|
|
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
this.wasRiding = false;
|
|
}
|
|
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
byte b2 = Mth.packDegrees(this.entity.getYHeadRot());
|
|
if (Math.abs(b2 - this.lastSentYHeadRot) >= 1) {
|
|
this.broadcast.accept(new ClientboundRotateHeadPacket(this.entity, b2));
|
|
this.lastSentYHeadRot = b2;
|
|
}
|
|
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
this.entity.hasImpulse = false;
|
|
this.forceStateResync = false; // Paper - fix desync when a player is added to the tracker
|
|
}
|
|
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
this.tickCount++;
|
|
if (this.entity.hurtMarked) {
|
|
// CraftBukkit start - Create PlayerVelocity event
|
|
boolean cancelled = false;
|
|
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
if (this.entity instanceof ServerPlayer) {
|
|
org.bukkit.entity.Player player = (org.bukkit.entity.Player) this.entity.getBukkitEntity();
|
|
org.bukkit.util.Vector velocity = player.getVelocity();
|
|
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
org.bukkit.event.player.PlayerVelocityEvent event = new org.bukkit.event.player.PlayerVelocityEvent(player, velocity.clone());
|
|
if (!event.callEvent()) {
|
|
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));
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
}
|
|
}
|
|
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ public void leafSendChanges(org.dreeam.leaf.async.tracker.TrackerCtx ctx, ChunkMap.TrackedEntity trackedEntity) {
|
|
+ // 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
|
|
+ trackedEntity.leafBroadcastIgnorePlayers(ctx, 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 (!trackedEntity.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);
|
|
+ }
|
|
+ this.leafSendDirtyEntityData(ctx, trackedEntity);
|
|
+ }
|
|
+
|
|
+ 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
|
|
+ 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) {
|
|
+ trackedEntity.leafBroadcast(ctx, new ClientboundMoveEntityPacket.Rot(this.entity.getId(), b, b1, this.entity.onGround()));
|
|
+ this.lastSentYRot = b;
|
|
+ this.lastSentXRot = b1;
|
|
+ }
|
|
+
|
|
+ this.positionCodec.setBase(this.entity.trackingPosition());
|
|
+ this.leafSendDirtyEntityData(ctx, trackedEntity);
|
|
+ this.wasRiding = true;
|
|
+ } else if (this.entity instanceof AbstractMinecart abstractMinecart
|
|
+ && abstractMinecart.getBehavior() instanceof NewMinecartBehavior newMinecartBehavior) {
|
|
+ this.leafHandleMinecartPosRot(ctx, trackedEntity, 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;
|
|
+ if (this.forceStateResync || this.entity.getRequiresPrecisePosition() // Paper - fix desync when a player is added to the tracker
|
|
+ || flag5
|
|
+ || this.teleportDelay > 400
|
|
+ || this.wasRiding
|
|
+ || this.wasOnGround != this.entity.onGround()) {
|
|
+ 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 && 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) {
|
|
+ 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));
|
|
+ }
|
|
+ }
|
|
+ } // SparklyPaper end
|
|
+ }
|
|
+
|
|
+ if (packet != null) {
|
|
+ trackedEntity.leafBroadcast(ctx, packet);
|
|
+ }
|
|
+
|
|
+ this.leafSendDirtyEntityData(ctx, trackedEntity);
|
|
+ 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) {
|
|
+ trackedEntity.leafBroadcast(ctx, new ClientboundRotateHeadPacket(this.entity, b2));
|
|
+ this.lastSentYHeadRot = b2;
|
|
+ }
|
|
+
|
|
+ this.entity.hasImpulse = false;
|
|
+ this.forceStateResync = false; // Paper - fix desync when a player is added to the tracker
|
|
+ }
|
|
+
|
|
+ this.tickCount++;
|
|
+ if (this.entity.hurtMarked) {
|
|
+ if (this.entity instanceof ServerPlayer serverPlayer) {
|
|
+ ctx.playerVelocity(serverPlayer);
|
|
+ } else {
|
|
+ this.entity.hurtMarked = false;
|
|
+ trackedEntity.leafBroadcastAndSend(ctx, 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)),
|
|
@@ -356,6 +570,39 @@ 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 trackedEntity, NewMinecartBehavior behavior, byte yRot, byte xRot, boolean dirty) {
|
|
+ this.leafSendDirtyEntityData(ctx, trackedEntity);
|
|
+ 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) {
|
|
+ trackedEntity
|
|
+ .leafBroadcast(ctx,
|
|
+ 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 {
|
|
+ trackedEntity.leafBroadcast(ctx, 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) {
|
|
this.entity.stopSeenByPlayer(player);
|
|
player.connection.send(new ClientboundRemoveEntitiesPacket(this.entity.getId()));
|
|
@@ -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;
|
|
+ List<Packet<? super ClientGamePacketListener>> list = new ArrayList<>();
|
|
+ this.sendPairingData0(player, list::add, true);
|
|
+ ctx.send(connection, new ClientboundBundlePacket(list));
|
|
+ ctx.startSeenByPlayer(connection, this.entity);
|
|
+ }
|
|
+
|
|
public void sendPairingData(ServerPlayer player, Consumer<Packet<ClientGamePacketListener>> consumer) {
|
|
+ sendPairingData0(player, consumer, false);
|
|
+ }
|
|
+
|
|
+ private void sendPairingData0(ServerPlayer player, Consumer<Packet<ClientGamePacketListener>> consumer, boolean async) {
|
|
if (this.entity.isRemoved()) {
|
|
+ // Leaf end - Multithreaded tracker
|
|
+
|
|
// CraftBukkit start - Remove useless error spam, just return
|
|
// LOGGER.warn("Fetching packet for removed entity {}", this.entity);
|
|
return;
|
|
@@ -407,7 +669,7 @@ public class ServerEntity {
|
|
if (!list.isEmpty()) {
|
|
consumer.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), list, true)); // Paper - data sanitization
|
|
}
|
|
- ((LivingEntity) this.entity).detectEquipmentUpdates(); // CraftBukkit - SPIGOT-3789: sync again immediately after sending
|
|
+ if (!async) { ((LivingEntity) this.entity).detectEquipmentUpdates(); } // CraftBukkit - SPIGOT-3789: sync again immediately after sending // Leaf - Multithreaded tracker
|
|
}
|
|
|
|
if (!this.entity.getPassengers().isEmpty()) {
|
|
@@ -443,6 +705,7 @@ public class ServerEntity {
|
|
return Mth.unpackDegrees(this.lastSentYHeadRot);
|
|
}
|
|
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
public void sendDirtyEntityData() {
|
|
SynchedEntityData entityData = this.entity.getEntityData();
|
|
List<SynchedEntityData.DataValue<?>> list = entityData.packDirty();
|
|
@@ -450,10 +713,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);
|
|
@@ -462,11 +727,38 @@ public class ServerEntity {
|
|
this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync));
|
|
}
|
|
|
|
- attributesToSync.clear();
|
|
+ // attributesToSync.clear(); // Leaf - Multithreaded tracker
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ public void leafSendDirtyEntityData(org.dreeam.leaf.async.tracker.TrackerCtx ctx, ChunkMap.TrackedEntity trackedEntity) {
|
|
+ SynchedEntityData entityData = this.entity.getEntityData();
|
|
+ List<SynchedEntityData.DataValue<?>> list = entityData.packDirty();
|
|
+ if (list != null) {
|
|
+ this.trackedDataValues = entityData.getNonDefaultValues();
|
|
+ ClientboundSetEntityDataPacket packet = new ClientboundSetEntityDataPacket(this.entity.getId(), list);
|
|
+ trackedEntity.leafBroadcastAndSend(ctx, packet);
|
|
+ }
|
|
+
|
|
+ if (this.entity instanceof LivingEntity) {
|
|
+ Set<AttributeInstance> attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync();
|
|
+ if (!attributesToSync.isEmpty()) {
|
|
+ // CraftBukkit start - Send scaled max health
|
|
+ if (this.entity instanceof ServerPlayer serverPlayer) {
|
|
+ serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ trackedEntity.leafBroadcastAndSend(ctx, new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync));
|
|
+ }
|
|
+
|
|
+ // attributesToSync.clear(); // Leaf - Multithreaded tracker
|
|
}
|
|
}
|
|
+ // Leaf end - Multithreaded tracker
|
|
|
|
private void broadcastAndSend(Packet<?> packet) {
|
|
+ // Leaf - Multithreaded tracker - diff on change
|
|
this.broadcast.accept(packet);
|
|
if (this.entity instanceof ServerPlayer) {
|
|
((ServerPlayer)this.entity).connection.send(packet);
|
|
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
|
index 08d12a1acc3a672a77daa15f82392cd603c30283..b0ac6de9e0f15d234781fc43dd6fd1d5cd6c5ddf 100644
|
|
--- a/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/net/minecraft/server/level/ServerLevel.java
|
|
@@ -216,6 +216,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 volatile @Nullable java.util.concurrent.Future<org.dreeam.leaf.async.tracker.TrackerCtx>[] trackerTask; // Leaf - Multithreaded tracker
|
|
|
|
@Override
|
|
public @Nullable LevelChunk getChunkIfLoaded(int x, int z) {
|
|
@@ -834,6 +835,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
this.tickBlockEntities();
|
|
}
|
|
// Paper - rewrite chunk system
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { org.dreeam.leaf.async.tracker.AsyncTracker.onEntitiesTickEnd(this); } // Leaf - Multithreaded tracker
|
|
}
|
|
|
|
@Override
|
|
@@ -2527,7 +2529,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
|
|
@Override
|
|
public LevelEntityGetter<Entity> getEntities() {
|
|
- org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot
|
|
+ //org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot // Leaf - Multithreaded tracker
|
|
return this.moonrise$getEntityLookup(); // Paper - rewrite chunk system
|
|
}
|
|
|
|
@@ -2799,7 +2801,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
}
|
|
|
|
map.carriedByPlayers.remove(player);
|
|
- if (map.carriedBy.removeIf(holdingPlayer -> holdingPlayer.player == player)) {
|
|
+ if (map.carriedBy.removeIf(holdingPlayer -> holdingPlayer != null && holdingPlayer.player == player)) { // Leaf - Multithreaded tracker
|
|
map.decorations.remove(player.getName().getString());
|
|
}
|
|
}
|
|
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
index 1556b2d4a758e3a15b6c4468bf994ea3781a4958..581791bf2892715543f003c36e301e690cc393f1 100644
|
|
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
@@ -1928,7 +1928,7 @@ public class ServerGamePacketListenerImpl
|
|
}
|
|
|
|
public void internalTeleport(PositionMoveRotation posMoveRotation, Set<Relative> relatives) {
|
|
- org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper
|
|
+ //org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper // Leaf - Multithreaded tracker
|
|
// 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 4d4efd979c4478c1571e7044f639afe08a7118d6..de4d13e7c6f57d6cc3a78b1b9ca914157820186c 100644
|
|
--- a/net/minecraft/world/entity/LivingEntity.java
|
|
+++ b/net/minecraft/world/entity/LivingEntity.java
|
|
@@ -1342,13 +1342,13 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
|
|
}
|
|
|
|
private void refreshDirtyAttributes() {
|
|
- Set<AttributeInstance> attributesToUpdate = this.getAttributes().getAttributesToUpdate();
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ int[] attributesToUpdate = this.getAttributes().getAttributesToUpdateIds();
|
|
|
|
- for (AttributeInstance attributeInstance : attributesToUpdate) {
|
|
- this.onAttributeUpdated(attributeInstance.getAttribute());
|
|
+ for (int attribute : attributesToUpdate) {
|
|
+ this.onAttributeUpdated(net.minecraft.core.registries.BuiltInRegistries.ATTRIBUTE.get(attribute).orElseThrow());
|
|
}
|
|
-
|
|
- attributesToUpdate.clear();
|
|
+ // Leaf end - Multithreaded tracker
|
|
}
|
|
|
|
protected void onAttributeUpdated(Holder<Attribute> attribute) {
|
|
diff --git a/net/minecraft/world/entity/ai/attributes/Attribute.java b/net/minecraft/world/entity/ai/attributes/Attribute.java
|
|
index f8419dde44ebc7324e783f8bee42132d5ec973c3..d7eef96e12c738cd06a8e16efe48b7673f2a8608 100644
|
|
--- a/net/minecraft/world/entity/ai/attributes/Attribute.java
|
|
+++ b/net/minecraft/world/entity/ai/attributes/Attribute.java
|
|
@@ -16,10 +16,15 @@ public class Attribute {
|
|
private boolean syncable;
|
|
private final String descriptionId;
|
|
private Attribute.Sentiment sentiment = Attribute.Sentiment.POSITIVE;
|
|
+ // Leaf start - Optimize AttributeMap
|
|
+ public final int uid;
|
|
+ private static int SIZE = 0;
|
|
+ // Leaf end - Optimize AttributeMap
|
|
|
|
protected Attribute(String descriptionId, double defaultValue) {
|
|
this.defaultValue = defaultValue;
|
|
this.descriptionId = descriptionId;
|
|
+ this.uid = SIZE++; // Leaf - Optimize AttributeMap
|
|
}
|
|
|
|
public double getDefaultValue() {
|
|
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
|
|
index 42ad600c6a5cb20e1d820f169f6a1a17ef3a5195..7f8eb388308806008805970d4d8ed329440380ee 100644
|
|
--- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
|
|
+++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
|
|
@@ -22,8 +22,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;
|
|
@@ -52,7 +68,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() {
|
|
@@ -140,8 +162,12 @@ public class AttributeInstance {
|
|
|
|
public double getValue() {
|
|
if (this.dirty) {
|
|
- this.cachedValue = this.calculateValue();
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ double value = this.calculateValue();
|
|
+ this.cachedValue = value;
|
|
this.dirty = false;
|
|
+ return value;
|
|
+ // Leaf end - Multithreaded tracker
|
|
}
|
|
|
|
return this.cachedValue;
|
|
@@ -184,7 +210,15 @@ public class AttributeInstance {
|
|
}
|
|
|
|
public AttributeInstance.Packed pack() {
|
|
- return new AttributeInstance.Packed(this.attribute, this.baseValue, List.copyOf(this.permanentModifiers.values()));
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
|
|
+ synchronized (this) {
|
|
+ return new AttributeInstance.Packed(this.attribute, this.baseValue, List.copyOf(this.permanentModifiers.values()));
|
|
+ }
|
|
+ } else {
|
|
+ return new AttributeInstance.Packed(this.attribute, this.baseValue, List.copyOf(this.permanentModifiers.values()));
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
}
|
|
|
|
public void apply(AttributeInstance.Packed instance) {
|
|
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
|
index 0ac398b8b10aae5e67a797b2991c66874003f282..d62c2644847173de8e11296508f111e786a95091 100644
|
|
--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
|
+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
|
@@ -15,9 +15,11 @@ import net.minecraft.resources.ResourceLocation;
|
|
|
|
public class AttributeMap {
|
|
// 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 Map<Holder<Attribute>, AttributeInstance> attributes = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap();
|
|
+ private final org.dreeam.leaf.util.map.AttributeInstanceSet attributesToSync = new org.dreeam.leaf.util.map.AttributeInstanceSet((org.dreeam.leaf.util.map.AttributeInstanceArrayMap) attributes);
|
|
+ private final org.dreeam.leaf.util.map.AttributeInstanceSet attributesToUpdate = new org.dreeam.leaf.util.map.AttributeInstanceSet((org.dreeam.leaf.util.map.AttributeInstanceArrayMap) attributes);
|
|
+ // Leaf end - Multithreaded tracker
|
|
// Gale end - Lithium - replace AI attributes with optimized collections
|
|
private final AttributeSupplier supplier;
|
|
private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables
|
|
@@ -32,28 +34,51 @@ public class AttributeMap {
|
|
this.supplier = defaultAttributes;
|
|
}
|
|
|
|
- private void onAttributeModified(AttributeInstance instance) {
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ private synchronized void onAttributeModified(AttributeInstance instance) {
|
|
this.attributesToUpdate.add(instance);
|
|
if (instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))) { // Purpur - Ridables
|
|
this.attributesToSync.add(instance);
|
|
}
|
|
}
|
|
|
|
- public Set<AttributeInstance> getAttributesToSync() {
|
|
- return this.attributesToSync;
|
|
+ private static final AttributeInstance[] EMPTY_ATTRIBUTE_INSTANCE = new AttributeInstance[0];
|
|
+ public synchronized Set<AttributeInstance> getAttributesToSync() {
|
|
+ var clone = it.unimi.dsi.fastutil.objects.ReferenceArraySet.ofUnchecked(attributesToSync.toArray(EMPTY_ATTRIBUTE_INSTANCE));
|
|
+ this.attributesToSync.clear();
|
|
+ return clone;
|
|
}
|
|
|
|
- public Set<AttributeInstance> getAttributesToUpdate() {
|
|
- return this.attributesToUpdate;
|
|
+ public synchronized Set<AttributeInstance> getAttributesToUpdate() {
|
|
+ var clone = it.unimi.dsi.fastutil.objects.ReferenceArraySet.ofUnchecked(attributesToUpdate.toArray(EMPTY_ATTRIBUTE_INSTANCE));
|
|
+ this.attributesToUpdate.clear();
|
|
+ return clone;
|
|
}
|
|
|
|
+ public synchronized int[] getAttributesToUpdateIds() {
|
|
+ int[] clone = attributesToUpdate.inner.toIntArray();
|
|
+ this.attributesToUpdate.clear();
|
|
+ return clone;
|
|
+ }
|
|
+ // Leaf end - Multithreaded tracker
|
|
+
|
|
public Collection<AttributeInstance> getSyncableAttributes() {
|
|
return this.attributes.values().stream().filter(instance -> instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))).collect(Collectors.toList()); // Purpur - Ridables
|
|
}
|
|
|
|
@Nullable
|
|
public AttributeInstance getInstance(Holder<Attribute> attribute) {
|
|
- return this.attributes.computeIfAbsent(attribute, holder -> this.supplier.createInstance(this::onAttributeModified, (Holder<Attribute>)holder));
|
|
+ // Leaf start - Multithreaded tracker
|
|
+ AttributeInstance v;
|
|
+ if ((v = this.attributes.get(attribute)) == null) {
|
|
+ AttributeInstance newValue;
|
|
+ if ((newValue = this.supplier.createInstance(this::onAttributeModified, attribute)) != null) {
|
|
+ attributes.put(attribute, newValue);
|
|
+ return newValue;
|
|
+ }
|
|
+ }
|
|
+ return v;
|
|
+ // Leaf end - Multithreaded tracker
|
|
}
|
|
|
|
public boolean hasAttribute(Holder<Attribute> attribute) {
|
|
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
|
|
index 24710041ccbc70e5506d8d89ae34f0141977f209..05de8a77b389691dd6986f36b4cb8cc0935e21e4 100644
|
|
--- a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
|
|
+++ b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
|
|
@@ -11,7 +11,7 @@ public class AttributeSupplier {
|
|
private final Map<Holder<Attribute>, AttributeInstance> instances;
|
|
|
|
AttributeSupplier(Map<Holder<Attribute>, AttributeInstance> instances) {
|
|
- this.instances = instances;
|
|
+ this.instances = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(instances); // Leaf - Optimize AttributeMap
|
|
}
|
|
|
|
public AttributeInstance getAttributeInstance(Holder<Attribute> attribute) {
|
|
@@ -41,7 +41,7 @@ public class AttributeSupplier {
|
|
}
|
|
|
|
@Nullable
|
|
- public AttributeInstance createInstance(Consumer<AttributeInstance> onDirty, Holder<Attribute> attribute) {
|
|
+ public AttributeInstance createInstance(Consumer<AttributeInstance> onDirty, Holder<Attribute> attribute) { // Leaf - Multithreaded tracker
|
|
AttributeInstance attributeInstance = this.instances.get(attribute);
|
|
if (attributeInstance == null) {
|
|
return null;
|
|
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
|