Yatopia/patches/Tuinity/patches/server/0047-Optimise-nearby-player...

477 lines
28 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/PlayerChunk.java b/src/main/java/net/minecraft/server/level/PlayerChunk.java
index 37c3617979da7839ad89883d9800c14c34e8b19b..e3dc57282a559f783c027780740e8089e022c838 100644
--- a/src/main/java/net/minecraft/server/level/PlayerChunk.java
+++ b/src/main/java/net/minecraft/server/level/PlayerChunk.java
@@ -82,6 +82,12 @@ public class PlayerChunk {
long key = net.minecraft.server.MCUtil.getCoordinateKey(this.location);
this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
+ // Tuinity start - optimise checkDespawn
+ Chunk chunk = this.getFullChunkIfCached();
+ if (chunk != null) {
+ chunk.updateGeneralAreaCache();
+ }
+ // Tuinity end - optimise checkDespawn
}
// Paper end - optimise isOutsideOfRange
// Paper start - optimize chunk status progression without jumping through thread pool
diff --git a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
index 3b727fcb05074dea7cc1689e8589d4f93d5ccceb..7897fe870234ddef12926d855a3f060f9a72b2ba 100644
--- a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
@@ -250,6 +250,12 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerViewDistanceTickMap;
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerViewDistanceNoTickMap;
// Paper end - no-tick view distance
+ // Tuinity start - optimise checkDespawn
+ public static final int GENERAL_AREA_MAP_SQUARE_RADIUS = 38;
+ 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;
+ // Tuinity end - optimise checkDespawn
void addPlayerToDistanceMaps(EntityPlayer player) {
com.tuinity.tuinity.util.TickThread.softEnsureTickThread("Cannot update distance maps off of the main thread"); // Tuinity
@@ -280,6 +286,9 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
this.playerViewDistanceBroadcastMap.add(player, chunkX, chunkZ, effectiveNoTickViewDistance + 1); // clients need an extra neighbour to render the full view distance configured
player.needsChunkCenterUpdate = false;
// Paper end - no-tick view distance
+ // Tuinity start - optimise checkDespawn
+ this.playerGeneralAreaMap.add(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS);
+ // Tuinity end - optimise checkDespawn
}
void removePlayerFromDistanceMaps(EntityPlayer player) {
@@ -298,6 +307,9 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
this.playerViewDistanceTickMap.remove(player);
this.playerViewDistanceNoTickMap.remove(player);
// Paper end - no-tick view distance
+ // Tuinity start - optimise checkDespawn
+ this.playerGeneralAreaMap.remove(player);
+ // Tuinity end - optimise checkDespawn
}
void updateMaps(EntityPlayer player) {
@@ -329,6 +341,9 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
this.playerViewDistanceBroadcastMap.update(player, chunkX, chunkZ, effectiveNoTickViewDistance + 1); // clients need an extra neighbour to render the full view distance configured
player.needsChunkCenterUpdate = false;
// Paper end - no-tick view distance
+ // Tuinity start - optimise checkDespawn
+ this.playerGeneralAreaMap.update(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS);
+ // Tuinity end - optimise checkDespawn
}
// Paper end
@@ -533,6 +548,23 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
this.dataRegionManager = new com.tuinity.tuinity.chunk.SingleThreadChunkRegionManager(this.world, 2, (1.0 / 3.0), 1, 6, "Data", DataRegionData::new, DataRegionSectionData::new);
this.regionManagers.add(this.dataRegionManager);
// Tuinity end
+ // Tuinity start - optimise checkDespawn
+ this.playerGeneralAreaMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
+ (EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> newState) -> {
+ Chunk chunk = PlayerChunkMap.this.world.getChunkProvider().getChunkAtIfCachedImmediately(rangeX, rangeZ);
+ if (chunk != null) {
+ chunk.updateGeneralAreaCache(newState);
+ }
+ },
+ (EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> newState) -> {
+ Chunk chunk = PlayerChunkMap.this.world.getChunkProvider().getChunkAtIfCachedImmediately(rangeX, rangeZ);
+ if (chunk != null) {
+ chunk.updateGeneralAreaCache(newState);
+ }
+ });
+ // Tuinity end - optimise checkDespawn
}
// Paper start - Chunk Prioritization
public void queueHolderUpdate(PlayerChunk playerchunk) {
diff --git a/src/main/java/net/minecraft/server/level/WorldServer.java b/src/main/java/net/minecraft/server/level/WorldServer.java
index fe32a457754d1dfcc37f7bf7ee6dd200ba7e323c..31276fe09f0dc0bf59b97d45b2404e39478663bb 100644
--- a/src/main/java/net/minecraft/server/level/WorldServer.java
+++ b/src/main/java/net/minecraft/server/level/WorldServer.java
@@ -414,6 +414,107 @@ public class WorldServer extends World implements GeneratorAccessSeed {
public long lastMidTickExecuteFailure;
// Tuinity end - execute chunk tasks mid tick
+ // Tuinity start - optimise checkDespawn
+ public final List<EntityPlayer> playersAffectingSpawning = new java.util.ArrayList<>();
+ // Tuinity end - optimise checkDespawn
+ // Tuinity start - optimise get nearest players for entity AI
+ public final EntityPlayer getNearestPlayer(net.minecraft.world.entity.ai.targeting.PathfinderTargetCondition condition, @Nullable EntityLiving source,
+ double centerX, double centerY, double centerZ) {
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> nearby;
+ if (source != null) {
+ Chunk chunk = source.getCurrentChunk();
+ if (chunk != null && (MathHelper.floor(centerX) >> 4) == chunk.locX &&
+ (MathHelper.floor(centerZ) >> 4) == chunk.locZ) {
+ nearby = chunk.getPlayerGeneralAreaCache();
+ } else {
+ nearby = this.getChunkProvider().playerChunkMap.playerGeneralAreaMap.getObjectsInRange(MathHelper.floor(centerX) >> 4, MathHelper.floor(centerZ) >> 4);
+ }
+ } else {
+ nearby = this.getChunkProvider().playerChunkMap.playerGeneralAreaMap.getObjectsInRange(MathHelper.floor(centerX) >> 4, MathHelper.floor(centerZ) >> 4);
+ }
+
+ if (nearby == null) {
+ return null;
+ }
+
+ Object[] backingSet = nearby.getBackingSet();
+
+ double closestDistanceSquared = Double.MAX_VALUE;
+ EntityPlayer closest = null;
+
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
+ Object _player = backingSet[i];
+ if (!(_player instanceof EntityPlayer)) {
+ continue;
+ }
+ EntityPlayer player = (EntityPlayer)_player;
+
+ double distanceSquared = player.getDistanceSquared(centerX, centerY, centerZ);
+ if (distanceSquared < closestDistanceSquared && condition.test(source, player)) {
+ closest = player;
+ closestDistanceSquared = distanceSquared;
+ }
+ }
+
+ return closest;
+ }
+
+ @Override
+ public EntityHuman a(net.minecraft.world.entity.ai.targeting.PathfinderTargetCondition pathfindertargetcondition, EntityLiving entityliving) {
+ return this.getNearestPlayer(pathfindertargetcondition, entityliving, entityliving.locX(), entityliving.locY(), entityliving.locZ());
+ }
+
+ @Override
+ public EntityHuman a(net.minecraft.world.entity.ai.targeting.PathfinderTargetCondition pathfindertargetcondition, @Nullable EntityLiving entityliving, double d0, double d1, double d2) {
+ return this.getNearestPlayer(pathfindertargetcondition, entityliving, d0, d1, d2);
+ }
+
+ @Override
+ public EntityHuman a(net.minecraft.world.entity.ai.targeting.PathfinderTargetCondition pathfindertargetcondition, double d0, double d1, double d2) {
+ return this.getNearestPlayer(pathfindertargetcondition, null, d0, d1, d2);
+ }
+
+ @Override
+ public List<EntityHuman> a(net.minecraft.world.entity.ai.targeting.PathfinderTargetCondition condition, EntityLiving source, AxisAlignedBB axisalignedbb) {
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> nearby;
+ double centerX = (axisalignedbb.maxX + axisalignedbb.minX) * 0.5;
+ double centerZ = (axisalignedbb.maxZ + axisalignedbb.minZ) * 0.5;
+ if (source != null) {
+ Chunk chunk = source.getCurrentChunk();
+ if (chunk != null && (MathHelper.floor(centerX) >> 4) == chunk.locX &&
+ (MathHelper.floor(centerZ) >> 4) == chunk.locZ) {
+ nearby = chunk.getPlayerGeneralAreaCache();
+ } else {
+ nearby = this.getChunkProvider().playerChunkMap.playerGeneralAreaMap.getObjectsInRange(MathHelper.floor(centerX) >> 4, MathHelper.floor(centerZ) >> 4);
+ }
+ } else {
+ nearby = this.getChunkProvider().playerChunkMap.playerGeneralAreaMap.getObjectsInRange(MathHelper.floor(centerX) >> 4, MathHelper.floor(centerZ) >> 4);
+ }
+
+ List<EntityHuman> 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 EntityPlayer)) {
+ continue;
+ }
+ EntityPlayer player = (EntityPlayer)_player;
+
+ if (axisalignedbb.contains(player.locX(), player.locY(), player.locZ()) && condition.test(source, player)) {
+ ret.add(player);
+ }
+ }
+
+ return ret;
+ }
+ // Tuinity end - optimise get nearest players for entity AI
+
// Add env and gen to constructor, WorldData -> WorldDataServer
public WorldServer(MinecraftServer minecraftserver, Executor executor, Convertable.ConversionSession convertable_conversionsession, IWorldDataServer iworlddataserver, ResourceKey<World> resourcekey, DimensionManager dimensionmanager, WorldLoadListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List<MobSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) {
super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getMethodProfiler, false, flag, i, gen, env, executor); // Paper pass executor
@@ -774,6 +875,14 @@ public class WorldServer extends World implements GeneratorAccessSeed {
public void doTick(BooleanSupplier booleansupplier) {
GameProfilerFiller gameprofilerfiller = this.getMethodProfiler();
+ // Tuinity start - optimise checkDespawn
+ this.playersAffectingSpawning.clear();
+ for (EntityPlayer player : this.getPlayers()) {
+ if (net.minecraft.world.entity.IEntitySelector.affectsSpawning.test(player)) {
+ this.playersAffectingSpawning.add(player);
+ }
+ }
+ // Tuinity end - optimise checkDespawn
this.ticking = true;
gameprofilerfiller.enter("world border");
diff --git a/src/main/java/net/minecraft/world/entity/EntityInsentient.java b/src/main/java/net/minecraft/world/entity/EntityInsentient.java
index a246edd09854dabf095da75c9d200f5cf26e7138..aae13c2e6c2a30b69c33417932c6a4d0aefeb7f5 100644
--- a/src/main/java/net/minecraft/world/entity/EntityInsentient.java
+++ b/src/main/java/net/minecraft/world/entity/EntityInsentient.java
@@ -779,7 +779,13 @@ public abstract class EntityInsentient extends EntityLiving {
if (this.world.getDifficulty() == EnumDifficulty.PEACEFUL && this.L()) {
this.die();
} else if (!this.isPersistent() && !this.isSpecialPersistence()) {
- EntityHuman entityhuman = this.world.findNearbyPlayer(this, -1.0D, IEntitySelector.affectsSpawning); // Paper
+ // Tuinity start - optimise checkDespawn
+ net.minecraft.world.level.chunk.Chunk chunk = this.getCurrentChunk();
+ EntityHuman entityhuman = chunk == null || this.world.paperConfig.hardDespawnDistance >= (net.minecraft.server.level.PlayerChunkMap.GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE_SQUARED) ? this.world.findNearbyPlayer(this, -1.0D, IEntitySelector.affectsSpawning) : chunk.findNearestPlayer(this.locX(), this.locY(), this.locZ(), -1.0, IEntitySelector.affectsSpawning); // Paper
+ if (entityhuman == null) {
+ entityhuman = ((WorldServer)this.world).playersAffectingSpawning.isEmpty() ? null : ((WorldServer)this.world).playersAffectingSpawning.get(0);
+ }
+ // Tuinity end - optimise checkDespawn
if (entityhuman != null) {
double d0 = entityhuman.h((Entity) this); // CraftBukkit - decompile error
diff --git a/src/main/java/net/minecraft/world/level/IEntityAccess.java b/src/main/java/net/minecraft/world/level/IEntityAccess.java
index 7c9ed41ae67bce0e5e45b9c091180ca3c99668fc..a7f2304acf8ee0a15d6eae8c42060e003be13ae7 100644
--- a/src/main/java/net/minecraft/world/level/IEntityAccess.java
+++ b/src/main/java/net/minecraft/world/level/IEntityAccess.java
@@ -107,7 +107,7 @@ public interface IEntityAccess {
default EntityHuman findNearbyPlayer(Entity entity, double d0, @Nullable Predicate<Entity> predicate) { return this.findNearbyPlayer(entity.locX(), entity.locY(), entity.locZ(), d0, predicate); } // Paper
@Nullable default EntityHuman findNearbyPlayer(double d0, double d1, double d2, double d3, @Nullable Predicate<Entity> predicate) { return a(d0, d1, d2, d3, predicate); } // Paper - OBFHELPER
- @Nullable default EntityHuman a(double d0, double d1, double d2, double d3, @Nullable Predicate<Entity> predicate) { // Paper
+ @Nullable default EntityHuman a(double d0, double d1, double d2, double d3, @Nullable Predicate<Entity> predicate) { // Paper // Tuinity - diff on change, override in World - this should be "get closest player that matches predicate"
double d4 = -1.0D;
EntityHuman entityhuman = null;
Iterator iterator = this.getPlayers().iterator();
@@ -208,17 +208,17 @@ public interface IEntityAccess {
}
@Nullable
- default EntityHuman a(PathfinderTargetCondition pathfindertargetcondition, EntityLiving entityliving) {
+ default EntityHuman a(PathfinderTargetCondition pathfindertargetcondition, EntityLiving entityliving) { // Tuinity - diff on change, override in World - this should be "get closest player that matches path finder target condition"
return (EntityHuman) this.a(this.getPlayers(), pathfindertargetcondition, entityliving, entityliving.locX(), entityliving.locY(), entityliving.locZ());
}
@Nullable
- default EntityHuman a(PathfinderTargetCondition pathfindertargetcondition, EntityLiving entityliving, double d0, double d1, double d2) {
+ default EntityHuman a(PathfinderTargetCondition pathfindertargetcondition, EntityLiving entityliving, double d0, double d1, double d2) { // Tuinity - diff on change, override in World - this should be "get closest player that matches path finder target condition"
return (EntityHuman) this.a(this.getPlayers(), pathfindertargetcondition, entityliving, d0, d1, d2);
}
@Nullable
- default EntityHuman a(PathfinderTargetCondition pathfindertargetcondition, double d0, double d1, double d2) {
+ default EntityHuman a(PathfinderTargetCondition pathfindertargetcondition, double d0, double d1, double d2) { // Tuinity - diff on change, override in World - this should be "get closest player that matches path finder target condition"
return (EntityHuman) this.a(this.getPlayers(), pathfindertargetcondition, (EntityLiving) null, d0, d1, d2);
}
@@ -254,7 +254,7 @@ public interface IEntityAccess {
return t0;
}
- default List<EntityHuman> a(PathfinderTargetCondition pathfindertargetcondition, EntityLiving entityliving, AxisAlignedBB axisalignedbb) {
+ default List<EntityHuman> a(PathfinderTargetCondition pathfindertargetcondition, EntityLiving entityliving, AxisAlignedBB axisalignedbb) { // Tuinity - diff on change, override in World - this should be "get players that matches path finder target condition"
List<EntityHuman> list = Lists.newArrayList();
Iterator iterator = this.getPlayers().iterator();
diff --git a/src/main/java/net/minecraft/world/level/SpawnerCreature.java b/src/main/java/net/minecraft/world/level/SpawnerCreature.java
index 24771c3522ea74ac12058591137eafc21adf3762..b3c73c6b4cffa20813355c5f2132fe3eaa134007 100644
--- a/src/main/java/net/minecraft/world/level/SpawnerCreature.java
+++ b/src/main/java/net/minecraft/world/level/SpawnerCreature.java
@@ -250,7 +250,7 @@ public final class SpawnerCreature {
blockposition_mutableblockposition.d(l, i, i1);
double d0 = (double) l + 0.5D;
double d1 = (double) i1 + 0.5D;
- EntityHuman entityhuman = worldserver.a(d0, (double) i, d1, -1.0D, false);
+ EntityHuman entityhuman = worldserver.a(d0, (double) i, d1, 576.0D, false); // Tuinity - copied from below method for range, for the love of god we do not need to fucking find the closet player outside of this range - limiting range lets us use the distance map
if (entityhuman != null) {
double d2 = entityhuman.h(d0, (double) i, d1);
@@ -322,7 +322,7 @@ public final class SpawnerCreature {
}
private static boolean a(WorldServer worldserver, IChunkAccess ichunkaccess, BlockPosition.MutableBlockPosition blockposition_mutableblockposition, double d0) {
- if (d0 <= 576.0D) {
+ if (d0 <= 576.0D) { // Tuinity - diff on change, copy into caller
return false;
} else if (worldserver.getSpawn().a((IPosition) (new Vec3D((double) blockposition_mutableblockposition.getX() + 0.5D, (double) blockposition_mutableblockposition.getY(), (double) blockposition_mutableblockposition.getZ() + 0.5D)), 24.0D)) {
return false;
diff --git a/src/main/java/net/minecraft/world/level/World.java b/src/main/java/net/minecraft/world/level/World.java
index 23db2bad910795b1335e247521d383c2dd87a23b..a802c45538943b570ca0daa3a40607dc80e696eb 100644
--- a/src/main/java/net/minecraft/world/level/World.java
+++ b/src/main/java/net/minecraft/world/level/World.java
@@ -183,6 +183,65 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
return typeKey;
}
+ // Tuinity start - optimise checkDespawn
+ public final List<EntityPlayer> getNearbyPlayers(@Nullable Entity source, double sourceX, double sourceY, double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) {
+ Chunk chunk;
+ if (source == null || (chunk = source.getCurrentChunk()) == null || maxRange < 0.0 || maxRange >= net.minecraft.server.level.PlayerChunkMap.GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE) {
+ return this.getNearbyPlayersSlow(source, sourceX, sourceY, sourceZ, maxRange, predicate);
+ }
+
+ List<EntityPlayer> ret = new java.util.ArrayList<>();
+ chunk.getNearestPlayers(sourceX, sourceY, sourceZ, predicate, maxRange, ret);
+ return ret;
+ }
+
+ private List<EntityPlayer> getNearbyPlayersSlow(@Nullable Entity source, double sourceX, double sourceY, double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) {
+ List<EntityPlayer> ret = new java.util.ArrayList<>();
+ double maxRangeSquared = maxRange * maxRange;
+
+ for (EntityPlayer player : (List<EntityPlayer>)this.getPlayers()) {
+ if ((maxRange < 0.0 || player.getDistanceSquared(sourceX, sourceY, sourceZ) < maxRangeSquared)) {
+ if (predicate == null || predicate.test(player)) {
+ ret.add(player);
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ private EntityPlayer getNearestPlayerSlow(@Nullable Entity source, double sourceX, double sourceY, double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) {
+ EntityPlayer closest = null;
+ double closestRangeSquared = maxRange < 0.0 ? Double.MAX_VALUE : maxRange * maxRange;
+
+ for (EntityPlayer player : (List<EntityPlayer>)this.getPlayers()) {
+ double distanceSquared = player.getDistanceSquared(sourceX, sourceY, sourceZ);
+ if (distanceSquared < closestRangeSquared && (predicate == null || predicate.test(player))) {
+ closest = player;
+ closestRangeSquared = distanceSquared;
+ }
+ }
+
+ return closest;
+ }
+
+
+ public final EntityPlayer getNearestPlayer(@Nullable Entity source, double sourceX, double sourceY, double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) {
+ Chunk chunk;
+ if (source == null || (chunk = source.getCurrentChunk()) == null || maxRange < 0.0 || maxRange >= net.minecraft.server.level.PlayerChunkMap.GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE) {
+ return this.getNearestPlayerSlow(source, sourceX, sourceY, sourceZ, maxRange, predicate);
+ }
+
+ return chunk.findNearestPlayer(sourceX, sourceY, sourceZ, maxRange, predicate);
+ }
+
+ @Override
+ public @Nullable EntityHuman a(double d0, double d1, double d2, double d3, @Nullable Predicate<Entity> predicate) {
+ return this.getNearestPlayer(null, d0, d1, d2, d3, predicate);
+ }
+
+ // Tuinity end - optimise checkDespawn
+
protected World(WorldDataMutable worlddatamutable, ResourceKey<World> resourcekey, final DimensionManager dimensionmanager, Supplier<GameProfilerFiller> supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.WorldDataServer) worlddatamutable).getName()); // Spigot
this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(((net.minecraft.world.level.storage.WorldDataServer) worlddatamutable).getName(), this.spigotConfig); // Paper
diff --git a/src/main/java/net/minecraft/world/level/chunk/Chunk.java b/src/main/java/net/minecraft/world/level/chunk/Chunk.java
index eb1e4a6c89b79fa1e2ec367eba441970c694f2d1..86d326d40a2957a12dc800560487af4bd272cdcb 100644
--- a/src/main/java/net/minecraft/world/level/chunk/Chunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/Chunk.java
@@ -157,6 +157,92 @@ public class Chunk implements IChunkAccess {
}
// Tuinity end - optimised entity slices
+ // Tuinity start - optimise checkDespawn
+ private boolean playerGeneralAreaCacheSet;
+ private com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> playerGeneralAreaCache;
+
+ public com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> getPlayerGeneralAreaCache() {
+ if (!this.playerGeneralAreaCacheSet) {
+ this.updateGeneralAreaCache();
+ }
+ return this.playerGeneralAreaCache;
+ }
+
+ public void updateGeneralAreaCache() {
+ this.updateGeneralAreaCache(((WorldServer)this.world).getChunkProvider().playerChunkMap.playerGeneralAreaMap.getObjectsInRange(this.coordinateKey));
+ }
+
+ public void updateGeneralAreaCache(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> value) {
+ this.playerGeneralAreaCacheSet = true;
+ this.playerGeneralAreaCache = value;
+ }
+
+ public EntityPlayer findNearestPlayer(double sourceX, double sourceY, double sourceZ, double maxRange, Predicate<Entity> predicate) {
+ if (!this.playerGeneralAreaCacheSet) {
+ this.updateGeneralAreaCache();
+ }
+
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> nearby = this.playerGeneralAreaCache;
+
+ if (nearby == null) {
+ return null;
+ }
+
+ Object[] backingSet = nearby.getBackingSet();
+ double closestDistance = maxRange < 0.0 ? Double.MAX_VALUE : maxRange * maxRange;
+ EntityPlayer closest = null;
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
+ Object _player = backingSet[i];
+ if (!(_player instanceof EntityPlayer)) {
+ continue;
+ }
+ EntityPlayer player = (EntityPlayer)_player;
+
+ double distance = player.getDistanceSquared(sourceX, sourceY, sourceZ);
+ if (distance < closestDistance && predicate.test(player)) {
+ closest = player;
+ closestDistance = distance;
+ }
+ }
+
+ return closest;
+ }
+
+ public void getNearestPlayers(double sourceX, double sourceY, double sourceZ, Predicate<Entity> predicate, double range, List<EntityPlayer> ret) {
+ if (!this.playerGeneralAreaCacheSet) {
+ this.updateGeneralAreaCache();
+ }
+
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> 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 EntityPlayer)) {
+ continue;
+ }
+ EntityPlayer player = (EntityPlayer)_player;
+
+ if (range >= 0.0) {
+ double distanceSquared = player.getDistanceSquared(sourceX, sourceY, sourceZ);
+ if (distanceSquared > rangeSquared) {
+ continue;
+ }
+ }
+
+ if (predicate == null || predicate.test(player)) {
+ ret.add(player);
+ }
+ }
+ }
+ // Tuinity end - optimise checkDespawn
+
public Chunk(World world, ChunkCoordIntPair chunkcoordintpair, BiomeStorage biomestorage, ChunkConverter chunkconverter, TickList<Block> ticklist, TickList<FluidType> ticklist1, long i, @Nullable ChunkSection[] achunksection, @Nullable Consumer<Chunk> consumer) {
this.sections = new ChunkSection[16];
this.e = Maps.newHashMap();