427 lines
24 KiB
Diff
427 lines
24 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <spottedleaf@spottedleaf.dev>
|
|
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 8bea90cb57f38f33e8b3162e24e353993a98ebbf..5b4c3ca92dffff876af18db106310cb14e8612b1 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
@@ -91,6 +91,12 @@ public class ChunkHolder {
|
|
this.chunkMap.needsChangeBroadcasting.add(this);
|
|
}
|
|
// Paper end - optimise chunk tick iteration
|
|
+ // Paper start - optimise checkDespawn
|
|
+ LevelChunk chunk = this.getFullChunkNowUnchecked();
|
|
+ if (chunk != null) {
|
|
+ chunk.updateGeneralAreaCache();
|
|
+ }
|
|
+ // Paper end - optimise checkDespawn
|
|
}
|
|
|
|
void onChunkRemove() {
|
|
@@ -101,6 +107,12 @@ public class ChunkHolder {
|
|
this.chunkMap.needsChangeBroadcasting.remove(this);
|
|
}
|
|
// Paper end - optimise chunk tick iteration
|
|
+ // Paper start - optimise checkDespawn
|
|
+ LevelChunk chunk = this.getFullChunkNowUnchecked();
|
|
+ if (chunk != null) {
|
|
+ chunk.removeGeneralAreaCache();
|
|
+ }
|
|
+ // 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 d2227b40aa6a5e49e30c1f33e407d8f25cf2be4e..1c5137be8c0935ae371d0b9e6a8f3d8bccd85d31 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -167,6 +167,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper
|
|
public final ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new ReferenceOpenHashSet<>();
|
|
|
|
+ // 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 {
|
|
@@ -244,6 +251,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
// Paper end - use distance map to optimise entity tracker
|
|
// Note: players need to be explicitly added to distance maps before they can be updated
|
|
this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
+ this.playerGeneralAreaMap.add(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS); // Paper - optimise checkDespawn
|
|
// Paper start - per player mob spawning
|
|
if (this.playerMobDistanceMap != null) {
|
|
this.playerMobDistanceMap.add(player, chunkX, chunkZ, this.distanceManager.getSimulationDistance());
|
|
@@ -262,6 +270,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
|
|
// Paper start - per player mob spawning
|
|
if (this.playerMobDistanceMap != null) {
|
|
this.playerMobDistanceMap.remove(player);
|
|
@@ -282,6 +291,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 start - per player mob spawning
|
|
if (this.playerMobDistanceMap != null) {
|
|
this.playerMobDistanceMap.update(player, chunkX, chunkZ, this.distanceManager.getSimulationDistance());
|
|
@@ -451,6 +461,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 69ecceda441d9a228c1ae401688ea7333287b80c..61964e73b020aa565a855d23f2f4da2da342c1e1 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -404,6 +404,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, IWorldDataServer -> WorldDataServer
|
|
public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
|
|
@@ -505,6 +582,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 a49dfe4f81d449c5dd7ba5b8f9af7fec5c54f5de..3646b969fa51b9683ab4137e530c3a6f6fc6c465 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Mob.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
|
|
@@ -802,7 +802,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().entities.spawning.despawnRanges.get(this.getType().getCategory()).hard() + 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 504455c626a9d22f552144a38861dc356fb02b99..13c4b7aee9b9802edbaf7e4df9e9355667e727bb 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -206,6 +206,69 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
return this.getChunkIfLoaded(chunkX, chunkZ) != null;
|
|
}
|
|
// 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
|
|
|
|
public abstract ResourceKey<LevelStem> getTypeKey();
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
index 4150e8cd7197eac53042d56f0a53a4951f8824ce..e31a2eea9a62ab2c0bed1a97dab6bae231b8cd8b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
@@ -259,7 +259,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);
|
|
@@ -332,7 +332,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().closerToCenterThan(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.isNaturalSpawningAllowed((BlockPos) pos));
|
|
+ return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerToCenterThan(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.isNaturalSpawningAllowed((BlockPos) pos)); // Paper - diff on change, copy into caller
|
|
}
|
|
|
|
private static Boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureManager 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 3591ac3b7823e109e01ebfa54ac70aa4deabd4f6..1a64558f8ebedbc4c47ad34787a45c0d89159360 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,98 @@ 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 removeGeneralAreaCache() {
|
|
+ this.playerGeneralAreaCacheSet = false;
|
|
+ this.playerGeneralAreaCache = null;
|
|
+ }
|
|
+
|
|
+ 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());
|