Yatopia/patches/server/0064-Async-entity-tracking.patch

579 lines
28 KiB
Diff
Raw Normal View History

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mykyta Komarn <nkomarn@hotmail.com>
Date: Sat, 3 Oct 2020 22:40:00 -0700
Subject: [PATCH] Async entity tracking
Entity tracking is done the same way, but asynchronously.
Lowers the MSPT and NSPT by a lot.
The changes made:
Synchronised the trackedEntities map in PlayerChunkMap, also added additional lock to it.
Created a tracker executor which is processing the track queue asynchronously.
A minor change was to replace 2 array lists with our GlueList implementation.
Certain stuff had to be ensured to be called on the main thread when the tracker entry was ticked due to how
the game's code works and due to how bukkit's event system works.
Entity tracking is some of the heavy stuff the server does. Making this async is trivial for making the performance
go up.
Co-authored-by: Ivan Pekov <ivan@mrivanplays.com>
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
index b43b02c0bdd5dbf0b7d30de90bdc2f74c015ecc8..d3d1120395ee0e5be781febefa502c40ad9dacdd 100644
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
@@ -49,6 +49,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
public final MinecraftServer server;
public final PlayerInteractManager playerInteractManager;
public final Deque<Integer> removeQueue = new ArrayDeque<>(); // Paper
+ public final java.util.concurrent.locks.Lock removeQueueLock = new java.util.concurrent.locks.ReentrantLock(); // Yatopia
private final AdvancementDataPlayer advancementDataPlayer;
private final ServerStatisticManager serverStatisticManager;
private float lastHealthScored = Float.MIN_VALUE;
@@ -459,6 +460,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.activeContainer = this.defaultContainer;
}
+ // Yatopia start
+ removeQueueLock.lock();
+ try {
+ // Yatopia end
while (!this.removeQueue.isEmpty()) {
int i = Math.min(this.removeQueue.size(), Integer.MAX_VALUE);
int[] aint = new int[i];
@@ -479,6 +484,9 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.playerConnection.sendPacket(new PacketPlayOutEntityDestroy(aint));
}
+ } finally { // Yatopia start
+ removeQueueLock.unlock();
+ } // Yatopia end
Entity entity = this.getSpecatorTarget();
@@ -1545,9 +1553,16 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
// Applies to the same player, so we need to not duplicate our removal queue. The rest of this method does "resetting"
// type logic so it does need to be called, maybe? This is silly.
// this.removeQueue.addAll(entityplayer.removeQueue);
+ // Yatopia start
+ removeQueueLock.lock();
+ try {
+ // Yatopia end
if (this.removeQueue != entityplayer.removeQueue) {
this.removeQueue.addAll(entityplayer.removeQueue);
}
+ } finally { // Yatopia start
+ removeQueueLock.unlock();
+ } // Yatopia end
// Paper end
this.cd = entityplayer.cd;
this.ci = entityplayer.ci;
@@ -1733,13 +1748,27 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
if (entity instanceof EntityHuman) {
this.playerConnection.sendPacket(new PacketPlayOutEntityDestroy(new int[]{entity.getId()}));
} else {
+ // Yatopia start
+ removeQueueLock.lock();
+ try {
+ // Yatopia end
this.removeQueue.add((Integer) entity.getId()); // CraftBukkit - decompile error
+ } finally { // Yatopia start
+ removeQueueLock.unlock();
+ } // Yatopia end
}
}
public void d(Entity entity) {
+ // Yatopia start
+ removeQueueLock.lock();
+ try {
+ // Yatopia end
this.removeQueue.remove((Integer) entity.getId()); // CraftBukkit - decompile error
+ } finally { // Yatopia start
+ removeQueueLock.unlock();
+ } // Yatopia end
}
@Override
@@ -1801,10 +1830,18 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.getBukkitEntity().teleport(new Location(newSpectatorTarget.getWorld().getWorld(), newSpectatorTarget.locX(), newSpectatorTarget.locY(), newSpectatorTarget.locZ(), this.yaw, this.pitch), TeleportCause.SPECTATE); // Correctly handle cross-world entities from api calls by using CB teleport
// Make sure we're tracking the entity before sending
- PlayerChunkMap.EntityTracker tracker = ((WorldServer)newSpectatorTarget.world).getChunkProvider().playerChunkMap.trackedEntities.get(newSpectatorTarget.getId());
+ // Yatopia start
+ PlayerChunkMap chunkMap = ((WorldServer)newSpectatorTarget.world).getChunkProvider().playerChunkMap;
+ chunkMap.trackedEntitiesLock.lock();
+ try {
+ PlayerChunkMap.EntityTracker tracker = chunkMap.trackedEntities.get(newSpectatorTarget.getId());
+ // Yatopia end
if (tracker != null) { // dumb plugins...
tracker.updatePlayer(this);
}
+ } finally { // Yatopia start
+ chunkMap.trackedEntitiesLock.unlock();
+ } // Yatopia end
} else {
this.playerConnection.teleport(this.spectatedEntity.locX(), this.spectatedEntity.locY(), this.spectatedEntity.locZ(), this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit
}
diff --git a/src/main/java/net/minecraft/server/EntityTNTPrimed.java b/src/main/java/net/minecraft/server/EntityTNTPrimed.java
index 3e71332c47000b21ff90aec6937f90dc639a41bd..b12aa1b3bf1397eea42cffb1c67c03448a56409b 100644
--- a/src/main/java/net/minecraft/server/EntityTNTPrimed.java
+++ b/src/main/java/net/minecraft/server/EntityTNTPrimed.java
@@ -87,18 +87,33 @@ public class EntityTNTPrimed extends Entity {
*/
// Send position and velocity updates to nearby players on every tick while the TNT is in water.
// This does pretty well at keeping their clients in sync with the server.
- PlayerChunkMap.EntityTracker ete = ((WorldServer)this.world).getChunkProvider().playerChunkMap.trackedEntities.get(this.getId());
+ // Yatopia start
+ PlayerChunkMap chunkMap = ((WorldServer)this.world).getChunkProvider().playerChunkMap;
+ chunkMap.trackedEntitiesLock.lock();
+ try {
+ PlayerChunkMap.EntityTracker ete = chunkMap.trackedEntities.get(this.getId());
+ // Yatopia end
if (ete != null) {
PacketPlayOutEntityVelocity velocityPacket = new PacketPlayOutEntityVelocity(this);
PacketPlayOutEntityTeleport positionPacket = new PacketPlayOutEntityTeleport(this);
+ // Yatopia start
+ ete.trackedPlayersLock.lock();
+ try {
+ // Yatopia end
ete.trackedPlayers.stream()
.filter(viewer -> (viewer.locX() - this.locX()) * (viewer.locY() - this.locY()) * (viewer.locZ() - this.locZ()) < 16 * 16)
.forEach(viewer -> {
viewer.playerConnection.sendPacket(velocityPacket);
viewer.playerConnection.sendPacket(positionPacket);
});
+ } finally { // Yatopia start
+ ete.trackedPlayersLock.unlock();
+ } // Yatopia end
}
+ } finally { // Yatopia start
+ chunkMap.trackedEntitiesLock.unlock();
+ } // Yatopia end
}
// Paper end
}
diff --git a/src/main/java/net/minecraft/server/EntityTrackerEntry.java b/src/main/java/net/minecraft/server/EntityTrackerEntry.java
index aea72b0db10eed151db18490c02f291c3cded92a..8e35ada472e9ba2e896c83cbbbf9a093063e6a65 100644
--- a/src/main/java/net/minecraft/server/EntityTrackerEntry.java
+++ b/src/main/java/net/minecraft/server/EntityTrackerEntry.java
@@ -39,6 +39,7 @@ public class EntityTrackerEntry {
private boolean r;
// CraftBukkit start
final Set<EntityPlayer> trackedPlayers; // Paper - private -> package
+ final java.util.concurrent.locks.Lock trackedPlayersLock = new java.util.concurrent.locks.ReentrantLock(); // Yatopia
// Paper start
private java.util.Map<EntityPlayer, Boolean> trackedPlayerMap = null;
@@ -74,7 +75,7 @@ public class EntityTrackerEntry {
public final void tick() { this.a(); } // Paper - OBFHELPER
public void a() {
- com.tuinity.tuinity.util.TickThread.softEnsureTickThread("Tracker update"); // Tuinity
+ // com.tuinity.tuinity.util.TickThread.softEnsureTickThread("Tracker update"); // Tuinity // Yatopia
List<Entity> list = this.tracker.getPassengers();
if (!list.equals(this.p)) {
@@ -89,18 +90,25 @@ public class EntityTrackerEntry {
if (this.tickCounter % 10 == 0 && itemstack.getItem() instanceof ItemWorldMap) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks
WorldMap worldmap = ItemWorldMap.getSavedMap(itemstack, this.b);
+ trackedPlayersLock.lock(); // Yatopia
+ try { // Yatopia
Iterator iterator = this.trackedPlayers.iterator(); // CraftBukkit
while (iterator.hasNext()) {
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
+ MCUtil.ensureMain(() -> { // Yatopia - ensure worldmap updates are done on the main thread
worldmap.a((EntityHuman) entityplayer, itemstack);
+ }); // Yatopia
Packet<?> packet = ((ItemWorldMap) itemstack.getItem()).a(itemstack, (World) this.b, (EntityHuman) entityplayer);
if (packet != null) {
entityplayer.playerConnection.sendPacket(packet);
}
}
+ } finally { // Yatopia start
+ trackedPlayersLock.unlock();
+ } // Yatopia end
}
this.c();
@@ -240,6 +248,7 @@ public class EntityTrackerEntry {
++this.tickCounter;
if (this.tracker.velocityChanged) {
// CraftBukkit start - Create PlayerVelocity event
+ MCUtil.ensureMain(() -> { // Yatopia - ensure this is called on the main thread
boolean cancelled = false;
if (this.tracker instanceof EntityPlayer) {
@@ -259,6 +268,7 @@ public class EntityTrackerEntry {
if (!cancelled) {
this.broadcastIncludingSelf(new PacketPlayOutEntityVelocity(this.tracker));
}
+ }); // Yatopia
// CraftBukkit end
this.tracker.velocityChanged = false;
}
@@ -282,7 +292,9 @@ public class EntityTrackerEntry {
// Purpur end
public void a(EntityPlayer entityplayer) {
+ MCUtil.ensureMain(() -> { // Yatopia - ensure main
this.tracker.c(entityplayer);
+ }); // Yatopia
entityplayer.c(this.tracker);
}
@@ -291,7 +303,9 @@ public class EntityTrackerEntry {
entityplayer.playerConnection.getClass();
this.a(playerconnection::sendPacket, entityplayer); // CraftBukkit - add player
+ MCUtil.ensureMain(() -> { // Yatopia - ensure main
this.tracker.b(entityplayer);
+ }); // Yatopia
entityplayer.d(this.tracker);
}
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
index 6c8cb39ac8786734cda994ef29ba74c685f3b9be..9cde23731ca8e22b5263784e65c7efb0ce5312f8 100644
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
@@ -11,6 +11,7 @@ import com.google.common.collect.Sets;
import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.util.Either;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; // Yatopia
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ByteMap;
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
@@ -113,10 +114,12 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
private final File w;
private final PlayerMap playerMap;
public final Int2ObjectMap<PlayerChunkMap.EntityTracker> trackedEntities;
+ public final java.util.concurrent.locks.Lock trackedEntitiesLock = new java.util.concurrent.locks.ReentrantLock(); // Yatopia
private final Long2ByteMap z;
private final Queue<Runnable> A; private final Queue<Runnable> getUnloadQueueTasks() { return this.A; } // Paper - OBFHELPER
int viewDistance; // Paper - private -> package private
public final com.destroystokyo.paper.util.PlayerMobDistanceMap playerMobDistanceMap; // Paper
+ private static final java.util.concurrent.ExecutorService trackerExecutor = java.util.concurrent.Executors.newCachedThreadPool(new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon(true).setNameFormat("Entity Tracker - %d").build()); // Yatopia
// CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
public final CallbackExecutor callbackExecutor = new CallbackExecutor();
@@ -301,7 +304,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
this.unloadQueue = new LongOpenHashSet();
this.u = new AtomicInteger();
this.playerMap = new PlayerMap();
- this.trackedEntities = new Int2ObjectOpenHashMap();
+ this.trackedEntities = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>()); // Yatopia
this.z = new Long2ByteOpenHashMap();
this.A = new com.destroystokyo.paper.utils.CachedSizeConcurrentLinkedQueue<>(); // Paper - need constant-time size()
this.definedStructureManager = definedstructuremanager;
@@ -2008,6 +2011,8 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
protected void addEntity(Entity entity) {
org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot
// Paper start - ignore and warn about illegal addEntity calls instead of crashing server
+ trackedEntitiesLock.lock(); // Yatopia
+ try { // Yatopia
if (!entity.valid || entity.world != this.world || this.trackedEntities.containsKey(entity.getId())) {
new Throwable("[ERROR] Illegal PlayerChunkMap::addEntity for world " + this.world.getWorld().getName()
+ ": " + entity + (this.trackedEntities.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""))
@@ -2047,10 +2052,15 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
}
}
+ } finally { // Yatopia start
+ trackedEntitiesLock.unlock();
+ } // Yatopia end
}
protected void removeEntity(Entity entity) {
org.spigotmc.AsyncCatcher.catchOp("entity untrack"); // Spigot
+ trackedEntitiesLock.lock(); // Yatopia
+ try { // Yatopia
if (entity instanceof EntityPlayer) {
EntityPlayer entityplayer = (EntityPlayer) entity;
@@ -2069,6 +2079,9 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
if (playerchunkmap_entitytracker1 != null) {
playerchunkmap_entitytracker1.a();
}
+ } finally { // Yatopia start
+ trackedEntitiesLock.unlock();
+ } // Yatopia end
entity.tracker = null; // Paper - We're no longer tracked
}
@@ -2077,12 +2090,14 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
this.world.timings.tracker1.startTiming();
try {
com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<Chunk> iterator = this.world.getChunkProvider().entityTickingChunks.iterator();
+ trackedEntitiesLock.lock(); // Yatopia
try {
while (iterator.hasNext()) {
Chunk chunk = iterator.next();
Entity[] entities = chunk.entities.getRawData();
for (int i = 0, len = chunk.entities.size(); i < len; ++i) {
Entity entity = entities[i];
+ if (entity == null) continue; // Yatopia
EntityTracker tracker = this.trackedEntities.get(entity.getId());
if (tracker != null) {
tracker.updatePlayers(tracker.tracker.getPlayersInTrackRange());
@@ -2092,6 +2107,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
}
} finally {
iterator.finishedIterating();
+ trackedEntitiesLock.unlock(); // Yatopia
}
} finally {
this.world.timings.tracker1.stopTiming();
@@ -2099,10 +2115,10 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
}
// Paper end - optimised tracker
- protected void g() {
+ protected void g() { // Yatopia - diff on change - make sure paper didn't drop tracker optimizations, otherwise kaboom
// Paper start - optimized tracker
if (true) {
- this.processTrackQueue();
+ trackerExecutor.execute(this::processTrackQueue); // Yatopia
return;
}
// Paper end - optimized tracker
@@ -2146,20 +2162,30 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
}
protected void broadcast(Entity entity, Packet<?> packet) {
+ trackedEntitiesLock.lock(); // Yatopia
+ try { // Yatopia
PlayerChunkMap.EntityTracker playerchunkmap_entitytracker = (PlayerChunkMap.EntityTracker) this.trackedEntities.get(entity.getId());
if (playerchunkmap_entitytracker != null) {
playerchunkmap_entitytracker.broadcast(packet);
}
+ } finally { // Yatopia start
+ trackedEntitiesLock.unlock();
+ } // Yatopia end
}
protected void broadcastIncludingSelf(Entity entity, Packet<?> packet) {
+ trackedEntitiesLock.lock(); // Yatopia
+ try { // Yatopia
PlayerChunkMap.EntityTracker playerchunkmap_entitytracker = (PlayerChunkMap.EntityTracker) this.trackedEntities.get(entity.getId());
if (playerchunkmap_entitytracker != null) {
playerchunkmap_entitytracker.broadcastIncludingSelf(packet);
}
+ } finally { // Yatopia start
+ trackedEntitiesLock.unlock();
+ } // Yatopia end
}
@@ -2272,11 +2298,13 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
entityplayer.a(chunk.getPos(), apacket[0], apacket[1]);
PacketDebug.a(this.world, chunk.getPos());
- List<Entity> list = Lists.newArrayList();
- List<Entity> list1 = Lists.newArrayList();
+ List<Entity> list = new net.yatopia.server.list.GlueList<>(); // Yatopia
+ List<Entity> list1 = new net.yatopia.server.list.GlueList<>(); // Yatopia
// Paper start - optimise entity tracker
// use the chunk entity list, not the whole trackedEntities map...
Entity[] entities = chunk.entities.getRawData();
+ trackedEntitiesLock.lock(); // Yatopia
+ try { // Yatopia
for (int i = 0, size = chunk.entities.size(); i < size; ++i) {
Entity entity = entities[i];
if (entity == entityplayer) {
@@ -2298,6 +2326,9 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
list1.add(entity);
}
}
+ } finally { // Yatopia start
+ trackedEntitiesLock.unlock();
+ } // Yatopia end
// Paper end - optimise entity tracker
Iterator iterator;
@@ -2345,6 +2376,7 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
// their first update (which is forced to have absolute coordinates), false afterward.
public java.util.Map<EntityPlayer, Boolean> trackedPlayerMap = new java.util.HashMap<>();
public Set<EntityPlayer> trackedPlayers = trackedPlayerMap.keySet();
+ public java.util.concurrent.locks.Lock trackedPlayersLock = new java.util.concurrent.locks.ReentrantLock(); // Yatopia
public EntityTracker(Entity entity, int i, int j, boolean flag) {
this.trackerEntry = new EntityTrackerEntry(PlayerChunkMap.this.world, entity, j, flag, this::broadcast, trackedPlayerMap); // CraftBukkit // Paper
@@ -2381,11 +2413,18 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
// stuff could have been removed, so we need to check the trackedPlayers set
// for players that were removed
+ // Yatopia start
+ trackedPlayersLock.lock();
+ try {
+ // Yatopia end
for (EntityPlayer player : this.trackedPlayers.toArray(new EntityPlayer[0])) { // avoid CME
if (newTrackerCandidates == null || !newTrackerCandidates.contains(player)) {
this.updatePlayer(player);
}
}
+ } finally { // Yatopia start
+ trackedPlayersLock.unlock();
+ } // Yatopia end
}
// Paper end - use distance map to optimise tracker
@@ -2398,6 +2437,10 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
}
public void broadcast(Packet<?> packet) {
+ // Yatopia start
+ trackedPlayersLock.lock();
+ try {
+ // Yatopia end
Iterator iterator = this.trackedPlayers.iterator();
while (iterator.hasNext()) {
@@ -2405,6 +2448,9 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
entityplayer.playerConnection.sendPacket(packet);
}
+ } finally { // Yatopia start
+ trackedPlayersLock.unlock();
+ } // Yatopia end
}
@@ -2417,6 +2463,10 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
}
public void a() {
+ // Yatopia start
+ trackedPlayersLock.lock();
+ try {
+ // Yatopia end
Iterator iterator = this.trackedPlayers.iterator();
while (iterator.hasNext()) {
@@ -2424,19 +2474,29 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
this.trackerEntry.a(entityplayer);
}
+ } finally { // Yatopia start
+ trackedPlayersLock.unlock();
+ } // Yatopia end
}
public void clear(EntityPlayer entityplayer) {
org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot
+ // Yatopia start
+ trackedPlayersLock.lock();
+ try {
+ // Yatopia end
if (this.trackedPlayers.remove(entityplayer)) {
this.trackerEntry.a(entityplayer);
}
+ } finally { // Yatopia start
+ trackedPlayersLock.unlock();
+ } // Yatopia end
}
public void updatePlayer(EntityPlayer entityplayer) {
- org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
+ // org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot // Yatopia
if (entityplayer != this.tracker) {
// Paper start - remove allocation of Vec3D here
//Vec3D vec3d = entityplayer.getPositionVector().d(this.tracker.getPositionVector()); // MC-155077, SPIGOT-5113
@@ -2447,6 +2507,10 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
int i = Math.min(this.b(), (PlayerChunkMap.this.viewDistance - 1) * 16);
boolean flag = vec3d_dx >= (double) (-i) && vec3d_dx <= (double) i && vec3d_dz >= (double) (-i) && vec3d_dz <= (double) i && this.tracker.a(entityplayer); // Paper - remove allocation of Vec3D here
+ // Yatopia start
+ trackedPlayersLock.lock();
+ try {
+ // Yatopia end
if (flag) {
boolean flag1 = this.tracker.attachedToPlayer;
@@ -2467,7 +2531,14 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
}
}
+ // Yatopia start
+ entityplayer.removeQueueLock.lock();
+ try {
+ // Yatopia end
entityplayer.removeQueue.remove(Integer.valueOf(this.tracker.getId()));
+ } finally { // Yatopia start
+ entityplayer.removeQueueLock.unlock();
+ } // Yatopia end
// CraftBukkit end
if (flag1 && this.trackedPlayerMap.putIfAbsent(entityplayer, true) == null) { // Paper
@@ -2476,6 +2547,9 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
} else if (this.trackedPlayers.remove(entityplayer)) {
this.trackerEntry.a(entityplayer);
}
+ } finally { // Yatopia start
+ trackedPlayersLock.unlock();
+ } // Yatopia end
}
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 7e91e95941039cce630ed3eb88e1919e1d08c091..e87c02eec6885805a86e67fada0cf033ced5f304 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -1253,10 +1253,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
private void unregisterPlayer(EntityPlayer other) {
PlayerChunkMap tracker = ((WorldServer) entity.world).getChunkProvider().playerChunkMap;
// Paper end
+ // Yatopia start
+ tracker.trackedEntitiesLock.lock();
+ try { // Yatopia end
PlayerChunkMap.EntityTracker entry = tracker.trackedEntities.get(other.getId());
if (entry != null) {
entry.clear(getHandle());
}
+ } finally { // Yatopia start
+ tracker.trackedEntitiesLock.unlock();
+ } // Yatopia end
// Remove the hidden player from this player user list, if they're on it
if (other.sentListPacket) {
@@ -1303,10 +1309,25 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
+ // Yatopia start
+ tracker.trackedEntitiesLock.lock();
+ try { // Yatopia end
PlayerChunkMap.EntityTracker entry = tracker.trackedEntities.get(other.getId());
- if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
+ if (entry != null) { // Yatopia - check moved down
+ // Yatopia start
+ entry.trackedPlayersLock.lock();
+ try {
+ if (!entry.trackedPlayers.contains(getHandle())) {
entry.updatePlayer(getHandle());
+ }
+ } finally {
+ entry.trackedPlayersLock.unlock();
+ }
+ // Yatopia end
}
+ } finally { // Yatopia start
+ tracker.trackedEntitiesLock.unlock();
+ } // Yatopia end
}
// Paper start
private void reregisterPlayer(EntityPlayer player) {