mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-28 11:29:11 +00:00
* 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
1213 lines
67 KiB
Diff
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
|