diff options
Diffstat (limited to 'patches/server/0777-Optimise-nearby-player-lookups.patch')
-rw-r--r-- | patches/server/0777-Optimise-nearby-player-lookups.patch | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/patches/server/0777-Optimise-nearby-player-lookups.patch b/patches/server/0777-Optimise-nearby-player-lookups.patch new file mode 100644 index 0000000000..7eacd2f196 --- /dev/null +++ b/patches/server/0777-Optimise-nearby-player-lookups.patch @@ -0,0 +1,408 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Thu, 27 Aug 2020 16:22:52 -0700 +Subject: [PATCH] Optimise nearby player lookups + +Use a distance map to map out close players. +Note that it's important that we cache the distance map value per chunk +since the penalty of a map lookup could outweigh the benefits of +searching less players (as it basically did in the outside range patch). + +diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java +index 1f602d50f3212078490c0092ceefd3b17e0b1532..825fdb0336b0388dbbc54c8da99781900612031c 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java ++++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java +@@ -83,6 +83,12 @@ public class ChunkHolder { + long key = net.minecraft.server.MCUtil.getCoordinateKey(this.pos); + this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key); + this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key); ++ // Paper start - optimise checkDespawn ++ LevelChunk chunk = this.getFullChunkUnchecked(); ++ if (chunk != null) { ++ chunk.updateGeneralAreaCache(); ++ } ++ // Paper end - optimise checkDespawn + } + // Paper end - optimise anyPlayerCloseEnoughForSpawning + long lastAutoSaveTime; // Paper - incremental autosave +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index 087eec200cec325edb11f7fbae1a81a216b019d6..86d751738ae82257b527f01b805c30d055ac85c9 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -159,6 +159,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + int viewDistance; + public final com.destroystokyo.paper.util.PlayerMobDistanceMap playerMobDistanceMap; // Paper + ++ // Paper start - optimise checkDespawn ++ public static final int GENERAL_AREA_MAP_SQUARE_RADIUS = 40; ++ public static final double GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE = 16.0 * (GENERAL_AREA_MAP_SQUARE_RADIUS - 1); ++ public static final double GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE_SQUARED = GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE * GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE; ++ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerGeneralAreaMap; ++ // Paper end - optimise checkDespawn ++ + // CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback() + public final CallbackExecutor callbackExecutor = new CallbackExecutor(); + public static final class CallbackExecutor implements java.util.concurrent.Executor, Runnable { +@@ -239,6 +246,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); + this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); + // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning ++ this.playerGeneralAreaMap.add(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS); // Paper - optimise checkDespawn + } + + void removePlayerFromDistanceMaps(ServerPlayer player) { +@@ -251,6 +259,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.playerMobSpawnMap.remove(player); + this.playerChunkTickRangeMap.remove(player); + // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning ++ this.playerGeneralAreaMap.remove(player); // Paper - optimise checkDespawns + } + + void updateMaps(ServerPlayer player) { +@@ -266,6 +275,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + // Paper end - use distance map to optimise entity tracker + this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning ++ this.playerGeneralAreaMap.update(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS); // Paper - optimise checkDespawn + } + // Paper end + // Paper start +@@ -421,6 +431,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + }); + // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning ++ // Paper start - optimise checkDespawn ++ this.playerGeneralAreaMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, ++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> { ++ LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ); ++ if (chunk != null) { ++ chunk.updateGeneralAreaCache(newState); ++ } ++ }, ++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> { ++ LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ); ++ if (chunk != null) { ++ chunk.updateGeneralAreaCache(newState); ++ } ++ }); ++ // Paper end - optimise checkDespawn + } + + protected ChunkGenerator generator() { +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 83517c4eaf419770178f0520210218e0a70c4642..0918bb28fd058e6b79f45993a46738a50b05b60a 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -399,6 +399,83 @@ public class ServerLevel extends Level implements WorldGenLevel { + return this.getServer().getPlayerList().getPlayer(uuid); + } + // Paper end ++ // Paper start - optimise checkDespawn ++ public final List<ServerPlayer> playersAffectingSpawning = new java.util.ArrayList<>(); ++ // Paper end - optimise checkDespawn ++ // Paper start - optimise get nearest players for entity AI ++ @Override ++ public final ServerPlayer getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions condition, @Nullable LivingEntity source, ++ double centerX, double centerY, double centerZ) { ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> nearby; ++ nearby = this.getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(Mth.floor(centerX) >> 4, Mth.floor(centerZ) >> 4); ++ ++ if (nearby == null) { ++ return null; ++ } ++ ++ Object[] backingSet = nearby.getBackingSet(); ++ ++ double closestDistanceSquared = Double.MAX_VALUE; ++ ServerPlayer closest = null; ++ ++ for (int i = 0, len = backingSet.length; i < len; ++i) { ++ Object _player = backingSet[i]; ++ if (!(_player instanceof ServerPlayer)) { ++ continue; ++ } ++ ServerPlayer player = (ServerPlayer)_player; ++ ++ double distanceSquared = player.distanceToSqr(centerX, centerY, centerZ); ++ if (distanceSquared < closestDistanceSquared && condition.test(source, player)) { ++ closest = player; ++ closestDistanceSquared = distanceSquared; ++ } ++ } ++ ++ return closest; ++ } ++ ++ @Override ++ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions pathfindertargetcondition, LivingEntity entityliving) { ++ return this.getNearestPlayer(pathfindertargetcondition, entityliving, entityliving.getX(), entityliving.getY(), entityliving.getZ()); ++ } ++ ++ @Override ++ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions pathfindertargetcondition, ++ double d0, double d1, double d2) { ++ return this.getNearestPlayer(pathfindertargetcondition, null, d0, d1, d2); ++ } ++ ++ @Override ++ public List<Player> getNearbyPlayers(net.minecraft.world.entity.ai.targeting.TargetingConditions condition, LivingEntity source, AABB axisalignedbb) { ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> nearby; ++ double centerX = (axisalignedbb.maxX + axisalignedbb.minX) * 0.5; ++ double centerZ = (axisalignedbb.maxZ + axisalignedbb.minZ) * 0.5; ++ nearby = this.getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(Mth.floor(centerX) >> 4, Mth.floor(centerZ) >> 4); ++ ++ List<Player> ret = new java.util.ArrayList<>(); ++ ++ if (nearby == null) { ++ return ret; ++ } ++ ++ Object[] backingSet = nearby.getBackingSet(); ++ ++ for (int i = 0, len = backingSet.length; i < len; ++i) { ++ Object _player = backingSet[i]; ++ if (!(_player instanceof ServerPlayer)) { ++ continue; ++ } ++ ServerPlayer player = (ServerPlayer)_player; ++ ++ if (axisalignedbb.contains(player.getX(), player.getY(), player.getZ()) && condition.test(source, player)) { ++ ret.add(player); ++ } ++ } ++ ++ return ret; ++ } ++ // Paper end - optimise get nearest players for entity AI + + // Add env and gen to constructor, WorldData -> WorldDataServer + public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, ServerLevelData iworlddataserver, ResourceKey<Level> resourcekey, DimensionType dimensionmanager, ChunkProgressListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List<CustomSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) { +@@ -487,6 +564,14 @@ public class ServerLevel extends Level implements WorldGenLevel { + } + + public void tick(BooleanSupplier shouldKeepTicking) { ++ // Paper start - optimise checkDespawn ++ this.playersAffectingSpawning.clear(); ++ for (ServerPlayer player : this.players) { ++ if (net.minecraft.world.entity.EntitySelector.affectsSpawning.test(player)) { ++ this.playersAffectingSpawning.add(player); ++ } ++ } ++ // Paper end - optimise checkDespawn + ProfilerFiller gameprofilerfiller = this.getProfiler(); + + this.handlingTick = true; +diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java +index 0a559b658cd52a1cce2895c6d9f96aa665a85c7b..8200e33ed4ebcae8a27cccf2a28daba5e10cf75d 100644 +--- a/src/main/java/net/minecraft/world/entity/Mob.java ++++ b/src/main/java/net/minecraft/world/entity/Mob.java +@@ -792,7 +792,12 @@ public abstract class Mob extends LivingEntity { + if (this.level.getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) { + this.discard(); + } else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) { +- Player entityhuman = this.level.findNearbyPlayer(this, -1.0D, EntitySelector.affectsSpawning); // Paper ++ // Paper start - optimise checkDespawn ++ Player entityhuman = this.level.findNearbyPlayer(this, level.paperConfig.hardDespawnDistances.getInt(this.getType().getCategory()) + 1, EntitySelector.affectsSpawning); // Paper ++ if (entityhuman == null) { ++ entityhuman = ((ServerLevel)this.level).playersAffectingSpawning.isEmpty() ? null : ((ServerLevel)this.level).playersAffectingSpawning.get(0); ++ } ++ // Paper end - optimise checkDespawn + + if (entityhuman != null) { + double d0 = entityhuman.distanceToSqr((Entity) this); +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 103428df78d1efe805ab425f1b4085077239bdf6..4247dcb003626535dbb997f48ad9f61380bd17e9 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -244,6 +244,69 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + return ret; + } + // Paper end ++ // Paper start - optimise checkDespawn ++ public final List<net.minecraft.server.level.ServerPlayer> getNearbyPlayers(@Nullable Entity source, double sourceX, double sourceY, ++ double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) { ++ LevelChunk chunk; ++ if (maxRange < 0.0 || maxRange >= net.minecraft.server.level.ChunkMap.GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE || ++ (chunk = (LevelChunk)this.getChunkIfLoadedImmediately(Mth.floor(sourceX) >> 4, Mth.floor(sourceZ) >> 4)) == null) { ++ return this.getNearbyPlayersSlow(source, sourceX, sourceY, sourceZ, maxRange, predicate); ++ } ++ ++ List<net.minecraft.server.level.ServerPlayer> ret = new java.util.ArrayList<>(); ++ chunk.getNearestPlayers(sourceX, sourceY, sourceZ, predicate, maxRange, ret); ++ return ret; ++ } ++ ++ private List<net.minecraft.server.level.ServerPlayer> getNearbyPlayersSlow(@Nullable Entity source, double sourceX, double sourceY, ++ double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) { ++ List<net.minecraft.server.level.ServerPlayer> ret = new java.util.ArrayList<>(); ++ double maxRangeSquared = maxRange * maxRange; ++ ++ for (net.minecraft.server.level.ServerPlayer player : (List<net.minecraft.server.level.ServerPlayer>)this.players()) { ++ if ((maxRange < 0.0 || player.distanceToSqr(sourceX, sourceY, sourceZ) < maxRangeSquared)) { ++ if (predicate == null || predicate.test(player)) { ++ ret.add(player); ++ } ++ } ++ } ++ ++ return ret; ++ } ++ ++ private net.minecraft.server.level.ServerPlayer getNearestPlayerSlow(@Nullable Entity source, double sourceX, double sourceY, ++ double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) { ++ net.minecraft.server.level.ServerPlayer closest = null; ++ double closestRangeSquared = maxRange < 0.0 ? Double.MAX_VALUE : maxRange * maxRange; ++ ++ for (net.minecraft.server.level.ServerPlayer player : (List<net.minecraft.server.level.ServerPlayer>)this.players()) { ++ double distanceSquared = player.distanceToSqr(sourceX, sourceY, sourceZ); ++ if (distanceSquared < closestRangeSquared && (predicate == null || predicate.test(player))) { ++ closest = player; ++ closestRangeSquared = distanceSquared; ++ } ++ } ++ ++ return closest; ++ } ++ ++ ++ public final net.minecraft.server.level.ServerPlayer getNearestPlayer(@Nullable Entity source, double sourceX, double sourceY, ++ double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) { ++ LevelChunk chunk; ++ if (maxRange < 0.0 || maxRange >= net.minecraft.server.level.ChunkMap.GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE || ++ (chunk = (LevelChunk)this.getChunkIfLoadedImmediately(Mth.floor(sourceX) >> 4, Mth.floor(sourceZ) >> 4)) == null) { ++ return this.getNearestPlayerSlow(source, sourceX, sourceY, sourceZ, maxRange, predicate); ++ } ++ ++ return chunk.findNearestPlayer(sourceX, sourceY, sourceZ, maxRange, predicate); ++ } ++ ++ @Override ++ public @Nullable Player getNearestPlayer(double d0, double d1, double d2, double d3, @Nullable Predicate<Entity> predicate) { ++ return this.getNearestPlayer(null, d0, d1, d2, d3, predicate); ++ } ++ // Paper end - optimise checkDespawn + + protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, final DimensionType dimensionmanager, Supplier<ProfilerFiller> supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot +diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +index 9b13244571807907fc0e14463d746724b0713c19..49a0ceaf9a08f64f84f3925cfba3fab6bb034bae 100644 +--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java ++++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +@@ -275,7 +275,7 @@ public final class NaturalSpawner { + blockposition_mutableblockposition.set(l, i, i1); + double d0 = (double) l + 0.5D; + double d1 = (double) i1 + 0.5D; +- Player entityhuman = world.getNearestPlayer(d0, (double) i, d1, -1.0D, false); ++ Player entityhuman = (chunk instanceof LevelChunk) ? ((LevelChunk)chunk).findNearestPlayer(d0, i, d1, 576.0D, net.minecraft.world.entity.EntitySelector.NO_SPECTATORS) : world.getNearestPlayer(d0, (double) i, d1, -1.0D, false); // Paper - use chunk's player cache to optimize search in range + + if (entityhuman != null) { + double d2 = entityhuman.distanceToSqr(d0, (double) i, d1); +@@ -348,7 +348,7 @@ public final class NaturalSpawner { + } + + private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) { +- return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerThan((Position) (new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isPositionEntityTicking((BlockPos) pos)); ++ return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerThan((Position) (new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isPositionEntityTicking((BlockPos) pos)); // Paper - diff on change, copy into caller + } + + private static Boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) { // Paper +diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +index 8e03e63a00dd242791ba0d5a8a17922227b16165..4a9a1fef5603b073e6d2d12e3e8e5dca73a7bd1b 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -235,6 +235,93 @@ public class LevelChunk extends ChunkAccess { + } + } + // Paper end ++ // Paper start - optimise checkDespawn ++ private boolean playerGeneralAreaCacheSet; ++ private com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> playerGeneralAreaCache; ++ ++ public com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> getPlayerGeneralAreaCache() { ++ if (!this.playerGeneralAreaCacheSet) { ++ this.updateGeneralAreaCache(); ++ } ++ return this.playerGeneralAreaCache; ++ } ++ ++ public void updateGeneralAreaCache() { ++ this.updateGeneralAreaCache(((ServerLevel)this.level).getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(this.coordinateKey)); ++ } ++ ++ public void updateGeneralAreaCache(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> value) { ++ this.playerGeneralAreaCacheSet = true; ++ this.playerGeneralAreaCache = value; ++ } ++ ++ public net.minecraft.server.level.ServerPlayer findNearestPlayer(double sourceX, double sourceY, double sourceZ, ++ double maxRange, java.util.function.Predicate<Entity> predicate) { ++ if (!this.playerGeneralAreaCacheSet) { ++ this.updateGeneralAreaCache(); ++ } ++ ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> nearby = this.playerGeneralAreaCache; ++ ++ if (nearby == null) { ++ return null; ++ } ++ ++ Object[] backingSet = nearby.getBackingSet(); ++ double closestDistance = maxRange < 0.0 ? Double.MAX_VALUE : maxRange * maxRange; ++ net.minecraft.server.level.ServerPlayer closest = null; ++ for (int i = 0, len = backingSet.length; i < len; ++i) { ++ Object _player = backingSet[i]; ++ if (!(_player instanceof net.minecraft.server.level.ServerPlayer)) { ++ continue; ++ } ++ net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer)_player; ++ ++ double distance = player.distanceToSqr(sourceX, sourceY, sourceZ); ++ if (distance < closestDistance && predicate.test(player)) { ++ closest = player; ++ closestDistance = distance; ++ } ++ } ++ ++ return closest; ++ } ++ ++ public void getNearestPlayers(double sourceX, double sourceY, double sourceZ, java.util.function.Predicate<Entity> predicate, ++ double range, java.util.List<net.minecraft.server.level.ServerPlayer> ret) { ++ if (!this.playerGeneralAreaCacheSet) { ++ this.updateGeneralAreaCache(); ++ } ++ ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> nearby = this.playerGeneralAreaCache; ++ ++ if (nearby == null) { ++ return; ++ } ++ ++ double rangeSquared = range * range; ++ ++ Object[] backingSet = nearby.getBackingSet(); ++ for (int i = 0, len = backingSet.length; i < len; ++i) { ++ Object _player = backingSet[i]; ++ if (!(_player instanceof net.minecraft.server.level.ServerPlayer)) { ++ continue; ++ } ++ net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer)_player; ++ ++ if (range >= 0.0) { ++ double distanceSquared = player.distanceToSqr(sourceX, sourceY, sourceZ); ++ if (distanceSquared > rangeSquared) { ++ continue; ++ } ++ } ++ ++ if (predicate == null || predicate.test(player)) { ++ ret.add(player); ++ } ++ } ++ } ++ // Paper end - optimise checkDespawn + + public LevelChunk(ServerLevel world, ProtoChunk protoChunk, @Nullable LevelChunk.PostLoadProcessor entityLoader) { + this(world, protoChunk.getPos(), protoChunk.getUpgradeData(), protoChunk.unpackBlockTicks(), protoChunk.unpackFluidTicks(), protoChunk.getInhabitedTime(), protoChunk.getSections(), entityLoader, protoChunk.getBlendingData()); |