mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-19 15:09:25 +00:00
* Revert AI goal selector to vanilla behavior * remove config * Remove config & Update patch comments * rename * re apply
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
|