From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Samsuik Date: Mon, 27 May 2024 18:02:27 +0100 Subject: [PATCH] Async Entity Tracking diff --git a/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java index 41b9405d6759d865e0d14dd4f95163e9690e967d..9767c1fcdbd5737c31e36fd9c39e89631ce8ee1c 100644 --- a/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java +++ b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java @@ -26,7 +26,7 @@ public abstract class AreaMap { // we use linked for better iteration. // map of: coordinate to set of objects in coordinate - protected final Long2ObjectOpenHashMap> areaMap = new Long2ObjectOpenHashMap<>(1024, 0.7f); + protected Long2ObjectOpenHashMap> areaMap = new Long2ObjectOpenHashMap<>(1024, 0.7f); // Sakura - async entity tracking protected final PooledLinkedHashSets pooledHashSets; protected final ChangeCallback addCallback; diff --git a/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java index 46954db7ecd35ac4018fdf476df7c8020d7ce6c8..183c3d13835a4f4bf6e3a4fa6eb2df0f21b8c882 100644 --- a/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java +++ b/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java @@ -5,7 +5,7 @@ import net.minecraft.server.level.ServerPlayer; /** * @author Spottedleaf */ -public final class PlayerAreaMap extends AreaMap { +public class PlayerAreaMap extends AreaMap { // Sakura - async entity tracking public PlayerAreaMap() { super(); diff --git a/src/main/java/me/samsuik/sakura/player/tracking/AsyncEntityTracker.java b/src/main/java/me/samsuik/sakura/player/tracking/AsyncEntityTracker.java new file mode 100644 index 0000000000000000000000000000000000000000..ca24b6fb2d4e8d4ff284f6b19c971f186e00cc38 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/player/tracking/AsyncEntityTracker.java @@ -0,0 +1,83 @@ +package me.samsuik.sakura.player.tracking; + +import net.minecraft.server.level.ChunkMap; +import org.spigotmc.AsyncCatcher; + +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +public final class AsyncEntityTracker { + private static final int AVAILABLE_THREADS = Math.max(Runtime.getRuntime().availableProcessors() / 5 * 2, 1); // 0.4 + private static final ThreadFactory THREAD_FACTORY = EntityTrackerThread::new; + private static ExecutorService TRACKER_EXECUTOR = null; + + private final Queue tasks = new ArrayDeque<>(); + private final ChunkMap chunkMap; + private volatile boolean processingTick; + + public AsyncEntityTracker(ChunkMap chunkMap) { + this.chunkMap = chunkMap; + } + + private static void tryStartService() { + if (TRACKER_EXECUTOR == null) { + TRACKER_EXECUTOR = Executors.newFixedThreadPool(AVAILABLE_THREADS, THREAD_FACTORY); + } + } + + public void scheduleForNextCycle(Runnable runnable) { + AsyncCatcher.catchOp("scheduled task off-main"); + this.tasks.offer(runnable); + } + + private void runScheduledTasks() { + Runnable runnable; + while ((runnable = this.tasks.poll()) != null) { + runnable.run(); + } + } + + public void cycle() { + if (this.processingTick) { + // Could this cause issues with players on teleports? + return; // uh oh. + } + + tryStartService(); + this.processingTick = true; + this.runScheduledTasks(); + + List trackedEntities = new ArrayList<>(this.chunkMap.entityMap.values()); + + TRACKER_EXECUTOR.execute(() -> { + try { + this.processEntities(trackedEntities); + } finally { + this.processingTick = false; + } + }); + } + + private void processEntities(List trackedEntities) { + // update players (seenBy) + for (ChunkMap.TrackedEntity tracker : trackedEntities) { + if (!tracker.isActive) continue; // removed entity + synchronized (tracker.entity) { + if (tracker.shouldLookForPlayers()) { + tracker.updatePlayers(tracker.entity.getPlayersInTrackRange()); + } + } + } + // send changes + for (ChunkMap.TrackedEntity tracker : trackedEntities) { + if (!tracker.isActive) continue; // removed entity + tracker.seenByLock.readLock().lock(); + synchronized (tracker.entity) { + tracker.serverEntity.sendChanges(); + } + tracker.seenByLock.readLock().unlock(); + } + } +} diff --git a/src/main/java/me/samsuik/sakura/player/tracking/EntityTrackerThread.java b/src/main/java/me/samsuik/sakura/player/tracking/EntityTrackerThread.java new file mode 100644 index 0000000000000000000000000000000000000000..ac95e188335419cb3db37e62a3e83f87ffc8ee9d --- /dev/null +++ b/src/main/java/me/samsuik/sakura/player/tracking/EntityTrackerThread.java @@ -0,0 +1,13 @@ +package me.samsuik.sakura.player.tracking; + +import io.papermc.paper.util.TickThread; + +import java.util.concurrent.atomic.AtomicInteger; + +public final class EntityTrackerThread extends TickThread { + private static final AtomicInteger THREAD_COUNTER = new AtomicInteger(0); + + public EntityTrackerThread(Runnable runnable) { + super(runnable, "Entity Tracker Thread " + THREAD_COUNTER.getAndIncrement()); + } +} diff --git a/src/main/java/me/samsuik/sakura/player/tracking/ThreadSafePlayerAreaMap.java b/src/main/java/me/samsuik/sakura/player/tracking/ThreadSafePlayerAreaMap.java new file mode 100644 index 0000000000000000000000000000000000000000..d4f69edae2c5e2d10bd417ad82f8f1aec5121e2e --- /dev/null +++ b/src/main/java/me/samsuik/sakura/player/tracking/ThreadSafePlayerAreaMap.java @@ -0,0 +1,35 @@ +package me.samsuik.sakura.player.tracking; + +import com.destroystokyo.paper.util.misc.PlayerAreaMap; +import com.destroystokyo.paper.util.misc.PooledLinkedHashSets; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.minecraft.server.level.ServerPlayer; + +public final class ThreadSafePlayerAreaMap extends PlayerAreaMap { + public ThreadSafePlayerAreaMap(final PooledLinkedHashSets pooledHashSets) { + super(pooledHashSets); + this.areaMap = new SynchronizedLong2ObjectOpenHashMap<>(); + } + + private static class SynchronizedLong2ObjectOpenHashMap extends Long2ObjectOpenHashMap> { + @Override + public synchronized PooledLinkedHashSets.PooledObjectLinkedOpenHashSet put(long k, PooledLinkedHashSets.PooledObjectLinkedOpenHashSet ePooledObjectLinkedOpenHashSet) { + return super.put(k, ePooledObjectLinkedOpenHashSet); + } + + @Override + public synchronized PooledLinkedHashSets.PooledObjectLinkedOpenHashSet putIfAbsent(long k, PooledLinkedHashSets.PooledObjectLinkedOpenHashSet ePooledObjectLinkedOpenHashSet) { + return super.putIfAbsent(k, ePooledObjectLinkedOpenHashSet); + } + + @Override + public synchronized boolean remove(Object key, Object value) { + return super.remove(key, value); + } + + @Override + public synchronized PooledLinkedHashSets.PooledObjectLinkedOpenHashSet get(long k) { + return super.get(k); + } + } +} diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java index 64bf4398a8a0b429e5a7483cf8a24a02c58b7fb3..b2ddb8910fe20be83d4b67142854a008fbd26eb0 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -338,7 +338,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider int trackRange = (configuredSpigotValue >>> 4) + ((configuredSpigotValue & 15) != 0 ? 1 : 0); this.entityTrackerTrackRanges[ordinal] = trackRange; - this.playerEntityTrackerTrackMaps[ordinal] = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets); + this.playerEntityTrackerTrackMaps[ordinal] = new me.samsuik.sakura.player.tracking.ThreadSafePlayerAreaMap(this.pooledLinkedPlayerHashSets); // Sakura - async entity tracking } // Paper end - use distance map to optimise entity tracker } @@ -1095,7 +1095,16 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider entity.tracker = playerchunkmap_entitytracker; // Paper - Fast access to tracker this.entityMap.put(entity.getId(), playerchunkmap_entitytracker); + // Sakura start - async entity tracking + Runnable updatePlayers = () -> { playerchunkmap_entitytracker.updatePlayers(entity.getPlayersInTrackRange()); // Paper - don't search all players + }; + if (this.level.asyncEntityTracking) { + this.asyncEntityTracker.scheduleForNextCycle(updatePlayers); + } else { + updatePlayers.run(); + } + // Sakura end - async entity tracking if (entity instanceof ServerPlayer) { ServerPlayer entityplayer = (ServerPlayer) entity; @@ -1163,8 +1172,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } } // Paper end - optimised tracker - + // Sakura start - async entity tracking + private final me.samsuik.sakura.player.tracking.AsyncEntityTracker asyncEntityTracker = new me.samsuik.sakura.player.tracking.AsyncEntityTracker(this); protected void tick() { + if (this.level.asyncEntityTracking) { + this.asyncEntityTracker.cycle(); + return; + } + // Sakura end - async entity tracking // Paper start - optimized tracker if (true) { this.processTrackQueue(); @@ -1308,12 +1323,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider public class TrackedEntity { public final ServerEntity serverEntity; - final Entity entity; + public final Entity entity; // Sakura - package-protected -> public private final int range; SectionPos lastSectionPos; public final Set seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl private final int playerSearchInterval; // Sakura - reduce entity tracker player updates private Vec3 entityPosition; // Sakura - reduce entity tracker player updates + public final java.util.concurrent.locks.ReentrantReadWriteLock seenByLock = new java.util.concurrent.locks.ReentrantReadWriteLock(); // Sakura - async entity tracking + public volatile boolean isActive = true; // Sakura - async entity tracking public TrackedEntity(Entity entity, int i, int j, boolean flag) { this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit @@ -1343,7 +1360,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // Paper start - use distance map to optimise tracker com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet lastTrackerCandidates; - final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newTrackerCandidates) { + public final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newTrackerCandidates) { // Sakura - async entity tracking com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet oldTrackerCandidates = this.lastTrackerCandidates; this.lastTrackerCandidates = newTrackerCandidates; @@ -1355,7 +1372,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider continue; } ServerPlayer player = (ServerPlayer)raw; - this.updatePlayer(player); + this.sakura_updatePlayer(player); // Sakura - async entity tracking } } @@ -1370,7 +1387,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider for (ServerPlayerConnection conn : this.seenBy.toArray(new ServerPlayerConnection[0])) { // avoid CME if (newTrackerCandidates == null || !newTrackerCandidates.contains(conn.getPlayer())) { - this.updatePlayer(conn.getPlayer()); + this.sakura_updatePlayer(conn.getPlayer()); // Sakura - async entity tracking } } } @@ -1404,6 +1421,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcastRemoved() { + this.isActive = false; // Sakura - async entity tracking + this.seenByLock.readLock().lock(); // Sakura - async entity tracking Iterator iterator = this.seenBy.iterator(); while (iterator.hasNext()) { @@ -1411,19 +1430,27 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.serverEntity.removePairing(serverplayerconnection.getPlayer()); } + this.seenByLock.readLock().unlock(); // Sakura - async entity tracking } public void removePlayer(ServerPlayer player) { org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot + this.seenByLock.writeLock().lock(); // Sakura - async entity tracking if (this.seenBy.remove(player.connection)) { this.serverEntity.removePairing(player); } + this.seenByLock.writeLock().unlock(); // Sakura - async entity tracking } public void updatePlayer(ServerPlayer player) { org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot + // Sakura start - async entity tracking + this.sakura_updatePlayer(player); + } + private void sakura_updatePlayer(ServerPlayer player) { + // Sakura end - async entity tracking if (player != this.entity) { // Paper start - remove allocation of Vec3D here // Vec3 vec3d = player.position().subtract(this.entity.position()); @@ -1466,6 +1493,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider flag = false; } // CraftBukkit end + this.seenByLock.writeLock().lock(); // Sakura - async entity tracking if (flag) { if (this.seenBy.add(player.connection)) { // Paper start - entity tracking events @@ -1477,6 +1505,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } else if (this.seenBy.remove(player.connection)) { this.serverEntity.removePairing(player); } + this.seenByLock.writeLock().unlock(); // Sakura - async entity tracking } } diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java index 2a9a6a9f00343f614a0d2430095a17088861eb1f..8d27289aa81aa60ba6bdde57b74fc4a0a76df4e5 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -923,7 +923,11 @@ public class ServerLevel extends Level implements WorldGenLevel { // Sakura end gameprofilerfiller.push("tick"); + // Sakura start - async entity tracking + synchronized (entity) { this.guardEntityTick(this::tickNonPassenger, entity); + } + // Sakura end - async entity tracking gameprofilerfiller.pop(); } } diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java index d6e60e4e7b5410f30b47e6b9b57b390837368dfc..3583a897912f4ae15111c909284c9b98aa55954c 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -335,8 +335,10 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl this.player.xo = this.player.getX(); this.player.yo = this.player.getY(); this.player.zo = this.player.getZ(); + synchronized (this.player) { // Sakura - async entity tracking this.player.doTick(); this.player.absMoveTo(this.firstGoodX, this.firstGoodY, this.firstGoodZ, this.player.getYRot(), this.player.getXRot()); + } // Sakura - async entity tracking ++this.tickCount; this.knownMovePacketCount = this.receivedMovePacketCount; if (this.clientIsFloating && !this.player.isSleeping() && !this.player.isPassenger() && !this.player.isDeadOrDying()) { @@ -2090,11 +2092,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl this.player.disconnect(); // Paper start - Adventure + synchronized (this.player) { // Sakura - async entity tracking quitMessage = quitMessage == null ? this.server.getPlayerList().remove(this.player) : this.server.getPlayerList().remove(this.player, quitMessage); // Paper - pass in quitMessage to fix kick message not being used if ((quitMessage != null) && !quitMessage.equals(net.kyori.adventure.text.Component.empty())) { this.server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(quitMessage), false); // Paper end } + } // Sakura - async entity tracking // CraftBukkit end this.player.getTextFilter().leave(); } diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java index 08d33295967f66051b9e846d854ffac6fe885281..febc3242b6aa112b1fad1b5559e5ccf7b50b4adc 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -3418,7 +3418,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S list.add(passenger); } + synchronized (this) { // Sakura - async entity tracking this.passengers = ImmutableList.copyOf(list); + } // Sakura - async entity tracking } this.gameEvent(GameEvent.ENTITY_MOUNT, passenger); @@ -3460,6 +3462,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S return false; } // CraftBukkit end + synchronized (this) { // Sakura - async entity tracking if (this.passengers.size() == 1 && this.passengers.get(0) == entity) { this.passengers = ImmutableList.of(); } else { @@ -3467,6 +3470,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S return entity1 != entity; }).collect(ImmutableList.toImmutableList()); } + } // Sakura - async entity tracking entity.boardingCooldown = 60; this.gameEvent(GameEvent.ENTITY_DISMOUNT, entity); diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java index c2c0d80adb6fa8cb74fa5fe3ce5bc7ac0609abba..7c821e890243d7ab0975a78f29f107cb5199f973 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -466,11 +466,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { return x >= bb.minX && x <= bb.maxX && y >= bb.minY && y <= bb.maxY; } // Sakura end - physics version api + public final boolean asyncEntityTracking; // Sakura - async entity tracking protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, Supplier sakuraWorldConfigCreator, java.util.concurrent.Executor executor) { // Sakura // Paper - create paper world config; Async-Anti-Xray: Pass executor this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config this.sakuraConfig = sakuraWorldConfigCreator.get(); // Sakura + this.asyncEntityTracking = this.sakuraConfig().players.entityTracker.asyncTracking; // Sakura - async entity tracking this.generator = gen; this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); diff --git a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java index 45269115e63cfc3bd7dc740a5694e2cc7c35bcb1..fd782a72f934e2de943d664568ccc7293b980e69 100644 --- a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java +++ b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java @@ -63,7 +63,7 @@ public class MapItemSavedData extends SavedData { public final List carriedBy = Lists.newArrayList(); public final Map carriedByPlayers = Maps.newHashMap(); private final Map bannerMarkers = Maps.newHashMap(); - public final Map decorations = Maps.newLinkedHashMap(); + public final Map decorations = java.util.Collections.synchronizedMap(Maps.newLinkedHashMap()); // Sakura - async entity tracking private final Map frameMarkers = Maps.newHashMap(); private int trackedDecorationCount; private org.bukkit.craftbukkit.map.RenderData vanillaRender = new org.bukkit.craftbukkit.map.RenderData(); // Paper @@ -584,6 +584,7 @@ public class MapItemSavedData extends SavedData { // Paper start private void addSeenPlayers(java.util.Collection icons) { org.bukkit.entity.Player player = (org.bukkit.entity.Player) this.player.getBukkitEntity(); + synchronized (MapItemSavedData.this.decorations) { // Sakura - async entity tracking MapItemSavedData.this.decorations.forEach((name, mapIcon) -> { // If this cursor is for a player check visibility with vanish system org.bukkit.entity.Player other = org.bukkit.Bukkit.getPlayerExact(name); // Spigot @@ -591,6 +592,7 @@ public class MapItemSavedData extends SavedData { icons.add(mapIcon); } }); + } // Sakura - async entity tracking } private boolean shouldUseVanillaMap() { return mapView.getRenderers().size() == 1 && mapView.getRenderers().get(0).getClass() == org.bukkit.craftbukkit.map.CraftMapRenderer.class; diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 4731d10dd5e493af9564d38d8bf1ff223390bd75..b1904549b1b1180ebae822e63871f6e80d9100cc 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -715,9 +715,11 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { ChunkMap.TrackedEntity entityTracker = world.getChunkSource().chunkMap.entityMap.get(this.getEntityId()); if (entityTracker != null) { + entityTracker.seenByLock.readLock().lock(); // Sakura - async entity tracking for (ServerPlayerConnection connection : entityTracker.seenBy) { players.add(connection.getPlayer().getBukkitEntity()); } + entityTracker.seenByLock.readLock().unlock(); // Sakura - async entity tracking } return players.build(); @@ -1013,9 +1015,11 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { } // Paper start, resend possibly desynced entity instead of add entity packet + entityTracker.seenByLock.readLock().lock(); // Sakura - async entity tracking for (ServerPlayerConnection playerConnection : entityTracker.seenBy) { this.getHandle().getEntityData().resendPossiblyDesyncedEntity(playerConnection.getPlayer()); } + entityTracker.seenByLock.readLock().unlock(); // Sakura - async entity tracking // Paper end } @@ -1183,10 +1187,12 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { return java.util.Collections.emptySet(); } + this.entity.tracker.seenByLock.readLock().lock(); // Sakura - async entity tracking Set set = new java.util.HashSet<>(this.entity.tracker.seenBy.size()); for (net.minecraft.server.network.ServerPlayerConnection connection : this.entity.tracker.seenBy) { set.add(connection.getPlayer().getBukkitEntity().getPlayer()); } + this.entity.tracker.seenByLock.readLock().unlock(); // Sakura - async entity tracking return set; } // Paper end - tracked players API diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 29f0c4c3fd9185bf8768572c135b50a9db34dbbe..cd462589ab90bf8dfd4a213f49c1257c8152e137 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -2060,9 +2060,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player { } ChunkMap.TrackedEntity entry = tracker.entityMap.get(other.getId()); + entry.seenByLock.readLock().lock(); // Sakura - async entity tracking if (entry != null && !entry.seenBy.contains(this.getHandle().connection)) { entry.updatePlayer(this.getHandle()); } + entry.seenByLock.readLock().unlock(); // Sakura - async entity tracking this.server.getPluginManager().callEvent(new PlayerShowEntityEvent(this, entity)); } diff --git a/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java index 15e9dd8844f893de5e8372b847c9e8295d6f69ca..8948474a27b70ed00a2eb0fc6db3beadd2a083ee 100644 --- a/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java +++ b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java @@ -34,6 +34,7 @@ public class CraftMapRenderer extends MapRenderer { cursors.removeCursor(cursors.getCursor(0)); } + synchronized (this.worldMap.decorations) { // Sakura - async entity tracking for (String key : this.worldMap.decorations.keySet()) { // If this cursor is for a player check visibility with vanish system Player other = Bukkit.getPlayerExact((String) key); @@ -44,6 +45,7 @@ public class CraftMapRenderer extends MapRenderer { MapDecoration decoration = this.worldMap.decorations.get(key); cursors.addCursor(decoration.x(), decoration.y(), (byte) (decoration.rot() & 15), decoration.type().getIcon(), true, decoration.name() == null ? null : io.papermc.paper.adventure.PaperAdventure.asAdventure(decoration.name())); // Paper } + } // Sakura - async entity tracking } }