even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even even more patches

This commit is contained in:
Jason Penilla 2021-11-24 23:47:39 -08:00 committed by MiniDigger | Martin
parent f04f3321e3
commit b39fa92d5d
50 changed files with 2250 additions and 382 deletions

View file

@ -1,62 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mariell Hoversholm <proximyst@proximyst.com>
Date: Sun, 24 Oct 2021 16:19:26 -0400
Subject: [PATCH] Add Raw Byte Entity Serialization
diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
index c8c2e5bc2ec1d8d7ea445a1203c9d5a904dfc666..014c9984018ad5e51a26228a137e1ba4eb3e80c8 100644
--- a/src/main/java/org/bukkit/UnsafeValues.java
+++ b/src/main/java/org/bukkit/UnsafeValues.java
@@ -102,6 +102,14 @@ public interface UnsafeValues {
ItemStack deserializeItem(byte[] data);
+ byte[] serializeEntity(org.bukkit.entity.Entity entity);
+
+ default org.bukkit.entity.Entity deserializeEntity(byte[] data, World world) {
+ return deserializeEntity(data, world, false);
+ }
+
+ org.bukkit.entity.Entity deserializeEntity(byte[] data, World world, boolean preserveUUID);
+
/**
* Return the translation key for the Material, so the client can translate it into the active
* locale when using a {@link net.kyori.adventure.text.TranslatableComponent}.
diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
index 18795003815d5bb6e04a15256430f69a31b2ace5..bafad5764cc3933fcd9602d37bd2e68424cbd575 100644
--- a/src/main/java/org/bukkit/entity/Entity.java
+++ b/src/main/java/org/bukkit/entity/Entity.java
@@ -767,5 +767,32 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
* @return players in tracking range
*/
@NotNull Set<Player> getTrackedPlayers();
+
+ /**
+ * Spawns the entity in the world at the given {@link Location} with the default spawn reason.
+ * <p>
+ * This will not spawn the entity if the entity is already spawned or has previously been despawned.
+ * <p>
+ * Also, this method will fire the same events as a normal entity spawn.
+ *
+ * @param location The location to spawn the entity at.
+ * @return Whether the entity was successfully spawned.
+ */
+ public default boolean spawnAt(@NotNull Location location) {
+ return spawnAt(location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
+ }
+
+ /**
+ * Spawns the entity in the world at the given {@link Location} with the reason given.
+ * <p>
+ * This will not spawn the entity if the entity is already spawned or has previously been despawned.
+ * <p>
+ * Also, this method will fire the same events as a normal entity spawn.
+ *
+ * @param location The location to spawn the entity at.
+ * @param reason The reason for the entity being spawned.
+ * @return Whether the entity was successfully spawned.
+ */
+ public boolean spawnAt(@NotNull Location location, @NotNull org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason);
// Paper end
}

View file

@ -1,55 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Tue, 22 Sep 2020 01:49:19 -0700
Subject: [PATCH] Optimise non-flush packet sending
Places like entity tracking make heavy use of packet sending,
and internally netty will use some very expensive thread wakeup
calls when scheduling.
Thanks to various hacks in ProtocolLib as well as other
plugins, we cannot simply use a queue of packets to group
send on execute. We have to call execute for each packet.
Tux's suggestion here is exactly what was needed - tag
the Runnable indicating it should not make a wakeup call.
Big thanks to Tux for making this possible as I had given
up on this optimisation before he came along.
Locally this patch drops the entity tracker tick by a full 1.5x.
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index 300cd4d2861c7f155cc6a5bb5a0c47b0b77ff240..7c66d5d51efd3ec55f5170cf828db22e26131517 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -49,6 +49,8 @@ import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
+
+import io.netty.util.concurrent.AbstractEventExecutor; // Paper
public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
private static final float AVERAGE_PACKETS_SMOOTHING = 0.75F;
@@ -409,9 +411,19 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
if (this.channel.eventLoop().inEventLoop()) {
this.doSendPacket(packet, callback, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter
} else {
+ // Paper start - optimise packets that are not flushed
+ // note: since the type is not dynamic here, we need to actually copy the old executor code
+ // into two branches. On conflict, just re-copy - no changes were made inside the executor code.
+ if (!flush) {
+ AbstractEventExecutor.LazyRunnable run = () -> {
+ this.doSendPacket(packet, callback, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter
+ };
+ this.channel.eventLoop().execute(run);
+ } else { // Paper end - optimise packets that are not flushed
this.channel.eventLoop().execute(() -> {
- this.doSendPacket(packet, callback, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter
+ this.doSendPacket(packet, callback, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter // Paper - diff on change
});
+ } // Paper
}
}

View file

@ -1,430 +0,0 @@
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 4588ae8037407b81c99135863eb0c4e97c564c24..2a33071c4b69cb7b5a7e296e8fd903e3a528b210 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -242,6 +242,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 isOutsideOfRange
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 6522eb4b72c50be3ff7d1b094066a1cbec88634d..10d1e1f728519ad49379895c7fd3668badcbfd5a 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -217,6 +217,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap; // this map is absent from updateMaps since it's controlled at the start of the chunkproviderserver tick
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerChunkTickRangeMap;
// Paper end - optimise PlayerChunkMap#isOutsideRange
+ // 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
void addPlayerToDistanceMaps(ServerPlayer player) {
int chunkX = MCUtil.getChunkCoordinate(player.getX());
@@ -237,6 +243,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE);
// Paper end - optimise PlayerChunkMap#isOutsideRange
this.playerChunkManager.addPlayer(player); // Paper - replace chunk loader
+ // Paper start - optimise checkDespawn
+ this.playerGeneralAreaMap.add(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS);
+ // Paper end - optimise checkDespawn
}
void removePlayerFromDistanceMaps(ServerPlayer player) {
@@ -250,6 +259,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.playerChunkTickRangeMap.remove(player);
// Paper end - optimise PlayerChunkMap#isOutsideRange
this.playerChunkManager.removePlayer(player); // Paper - replace chunk loader
+ // Paper start - optimise checkDespawn
+ this.playerGeneralAreaMap.remove(player);
+ // Paper end - optimise checkDespawn
}
void updateMaps(ServerPlayer player) {
@@ -268,6 +280,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE);
// Paper end - optimise PlayerChunkMap#isOutsideRange
this.playerChunkManager.updatePlayer(player); // Paper - replace chunk loader
+ // Paper start - optimise checkDespawn
+ this.playerGeneralAreaMap.update(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS);
+ // Paper end - optimise checkDespawn
}
// Paper end
// Paper start
@@ -426,6 +441,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
});
// Paper end - optimise PlayerChunkMap#isOutsideRange
+ // 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
}
// Paper start - Chunk Prioritization
@@ -739,7 +771,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} else {
if (holder != null) {
holder.setTicketLevel(level);
- holder.updateRanges(); // Paper - optimise isOutsideOfRange
+ // Paper - move to correct place
}
if (holder != null) {
@@ -754,6 +786,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
holder = (ChunkHolder) this.pendingUnloads.remove(pos);
if (holder != null) {
holder.setTicketLevel(level);
+ holder.updateRanges(); // Paper - optimise isOutsideOfRange // Paper - move to correct place
} else {
holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this.queueSorter, this);
// Paper start
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index ed696ed93251e644c2b71e468e2556d24b273933..3afb8b91dceb86e88b848462a5b8dbd5c8365239 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -392,6 +392,83 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
}
// Paper end - rewrite ticklistserver
+ // 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) {
@@ -492,6 +569,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 5f4556505d831ea45e576a4c4e10f99e1353684e..55d07e70a67e08bab3a7a66076c980986736e5b8 100644
--- a/src/main/java/net/minecraft/world/entity/Mob.java
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
@@ -789,7 +789,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); // CraftBukkit - decompile error
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 64d5e71a8a26116385cee195d86fce1ef1574a8c..f936e9f9a9fa655fa997d6862b5ed54c04169d35 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -247,6 +247,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 - 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 4d8251a961a9c52456db997506dd9691beaec022..55dd04816886d27a62856ac952d2fc5d15bf40e6 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
@@ -267,7 +267,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);
@@ -340,7 +340,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 9e75ee7f43722f05dd5df4ef00f7d3e271f4c6e5..86686c24b0b7de4b4bfadbc77419a8872a8e86ee 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -369,6 +369,93 @@ public class LevelChunk implements 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 worldserver, ProtoChunk protoChunk, @Nullable Consumer<LevelChunk> consumer) {
this(worldserver, protoChunk.getPos(), protoChunk.getBiomes(), protoChunk.getUpgradeData(), protoChunk.getBlockTicks(), protoChunk.getLiquidTicks(), protoChunk.getInhabitedTime(), protoChunk.getSections(), consumer);

View file

@ -1,197 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 19 Jun 2021 10:54:52 -0700
Subject: [PATCH] Fix Codec log spam
Mojang did NOT add dataconverters for world gen configurations
that they CHANGED. So, the codec fails to parse old data.
This fixes two instances:
- IntProvider is new and Mojang did not account for old data.
Thankfully, only ColumnPlace needed to be special cased.
- TreeConfiguration had changes. Thankfully, they were
only renames for one value and thankfully defaults could
be provided for two new values (WITHOUT changing behavior).
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index c4117dcffd705d044f07eb5840a177b1b5825bb9..f80791bd383dc6dc4a9c1aac5f8e4c1561e33ad9 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -723,4 +723,70 @@ public final class MCUtil {
public static int getTicketLevelFor(net.minecraft.world.level.chunk.ChunkStatus status) {
return net.minecraft.server.level.ChunkMap.MAX_VIEW_DISTANCE + net.minecraft.world.level.chunk.ChunkStatus.getDistance(status);
}
+
+ public static <A> com.mojang.serialization.MapCodec<A> fieldWithFallbacks(com.mojang.serialization.Codec<A> codec, String name, String ...fallback) {
+ return com.mojang.serialization.MapCodec.of(
+ new com.mojang.serialization.codecs.FieldEncoder<>(name, codec),
+ new FieldFallbackDecoder<>(name, java.util.Arrays.asList(fallback), codec),
+ () -> "FieldFallback[" + name + ": " + codec.toString() + "]"
+ );
+ }
+
+ // This is likely a common occurrence, sadly
+ public static final class FieldFallbackDecoder<A> extends com.mojang.serialization.MapDecoder.Implementation<A> {
+ protected final String name;
+ protected final List<String> fallback;
+ private final com.mojang.serialization.Decoder<A> elementCodec;
+
+ public FieldFallbackDecoder(final String name, final List<String> fallback, final com.mojang.serialization.Decoder<A> elementCodec) {
+ this.name = name;
+ this.fallback = fallback;
+ this.elementCodec = elementCodec;
+ }
+
+ @Override
+ public <T> com.mojang.serialization.DataResult<A> decode(final com.mojang.serialization.DynamicOps<T> ops, final com.mojang.serialization.MapLike<T> input) {
+ T value = input.get(name);
+ if (value == null) {
+ for (String fall : fallback) {
+ value = input.get(fall);
+ if (value != null) {
+ break;
+ }
+ }
+ if (value == null) {
+ return com.mojang.serialization.DataResult.error("No key " + name + " in " + input);
+ }
+ }
+ return elementCodec.parse(ops, value);
+ }
+
+ @Override
+ public <T> java.util.stream.Stream<T> keys(final com.mojang.serialization.DynamicOps<T> ops) {
+ return java.util.stream.Stream.of(ops.createString(name));
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final FieldFallbackDecoder<?> that = (FieldFallbackDecoder<?>)o;
+ return java.util.Objects.equals(name, that.name) && java.util.Objects.equals(elementCodec, that.elementCodec)
+ && java.util.Objects.equals(fallback, that.fallback);
+ }
+
+ @Override
+ public int hashCode() {
+ return java.util.Objects.hash(name, fallback, elementCodec);
+ }
+
+ @Override
+ public String toString() {
+ return "FieldDecoder[" + name + ": " + elementCodec + ']';
+ }
+ }
}
diff --git a/src/main/java/net/minecraft/util/valueproviders/IntProvider.java b/src/main/java/net/minecraft/util/valueproviders/IntProvider.java
index 020a19cd683dd3779c5116d12b3cdcd3b3ca69b4..c81a0eec12436c10869bfdcc21af09173f438331 100644
--- a/src/main/java/net/minecraft/util/valueproviders/IntProvider.java
+++ b/src/main/java/net/minecraft/util/valueproviders/IntProvider.java
@@ -9,13 +9,44 @@ import net.minecraft.core.Registry;
public abstract class IntProvider {
private static final Codec<Either<Integer, IntProvider>> CONSTANT_OR_DISPATCH_CODEC = Codec.either(Codec.INT, Registry.INT_PROVIDER_TYPES.dispatch(IntProvider::getType, IntProviderType::codec));
- public static final Codec<IntProvider> CODEC = CONSTANT_OR_DISPATCH_CODEC.xmap((either) -> {
+ public static final Codec<IntProvider> CODEC_REAL = CONSTANT_OR_DISPATCH_CODEC.xmap((either) -> { // Paper - used by CODEC below
return either.map(ConstantInt::of, (intProvider) -> {
return intProvider;
});
}, (intProvider) -> {
return intProvider.getType() == IntProviderType.CONSTANT ? Either.left(((ConstantInt)intProvider).getValue()) : Either.right(intProvider);
});
+ // Paper start
+ public static final Codec<IntProvider> CODEC = new Codec<>() {
+ @Override
+ public <T> DataResult<com.mojang.datafixers.util.Pair<IntProvider, T>> decode(com.mojang.serialization.DynamicOps<T> ops, T input) {
+ /*
+ UniformInt:
+ count -> { (old format)
+ base, spread
+ } -> {UniformInt} { (new format & type)
+ base, base + spread
+ } */
+
+
+ if (ops.get(input, "base").result().isPresent() && ops.get(input, "spread").result().isPresent()) {
+ // detected old format
+ int base = ops.getNumberValue(ops.get(input, "base").result().get()).result().get().intValue();
+ int spread = ops.getNumberValue(ops.get(input, "spread").result().get()).result().get().intValue();
+ return DataResult.success(new com.mojang.datafixers.util.Pair<>(UniformInt.of(base, base + spread), input));
+ }
+
+ // not old format, forward to real codec
+ return CODEC_REAL.decode(ops, input);
+ }
+
+ @Override
+ public <T> DataResult<T> encode(IntProvider input, com.mojang.serialization.DynamicOps<T> ops, T prefix) {
+ // forward to real codec
+ return CODEC_REAL.encode(input, ops, prefix);
+ }
+ };
+ // Paper end
public static final Codec<IntProvider> NON_NEGATIVE_CODEC = codec(0, Integer.MAX_VALUE);
public static final Codec<IntProvider> POSITIVE_CODEC = codec(1, Integer.MAX_VALUE);
diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/blockplacers/ColumnPlacer.java b/src/main/java/net/minecraft/world/level/levelgen/feature/blockplacers/ColumnPlacer.java
index 05bba5410fbd9f8e333584ccbd65a909f3040322..82859e1b048ba8a96cc67e085e9ed01b6bd3a6cd 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/feature/blockplacers/ColumnPlacer.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/feature/blockplacers/ColumnPlacer.java
@@ -10,11 +10,28 @@ import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.state.BlockState;
public class ColumnPlacer extends BlockPlacer {
+ // Paper start
public static final Codec<ColumnPlacer> CODEC = RecordCodecBuilder.create((instance) -> {
- return instance.group(IntProvider.NON_NEGATIVE_CODEC.fieldOf("size").forGetter((columnPlacer) -> {
- return columnPlacer.size;
- })).apply(instance, ColumnPlacer::new);
+ return instance.group(
+ IntProvider.NON_NEGATIVE_CODEC.optionalFieldOf("size").forGetter((columnPlacer) -> {
+ return java.util.Optional.of(columnPlacer.size);
+ }),
+ Codec.INT.optionalFieldOf("min_size").forGetter((columnPlacer) -> {
+ return java.util.Optional.empty();
+ }),
+ Codec.INT.optionalFieldOf("extra_size").forGetter((columnPlacer) -> {
+ return java.util.Optional.empty();
+ })
+ ).apply(instance, ColumnPlacer::new);
});
+ public ColumnPlacer(java.util.Optional<IntProvider> size, java.util.Optional<Integer> minSize, java.util.Optional<Integer> extraSize) {
+ if (size.isPresent()) {
+ this.size = size.get();
+ } else {
+ this.size = net.minecraft.util.valueproviders.BiasedToBottomInt.of(minSize.get().intValue(), minSize.get().intValue() + extraSize.get().intValue());
+ }
+ }
+ // Paper end
private final IntProvider size;
public ColumnPlacer(IntProvider size) {
diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/configurations/TreeConfiguration.java b/src/main/java/net/minecraft/world/level/levelgen/feature/configurations/TreeConfiguration.java
index 5da68897148192905c2747676c1ee2ee649f923f..b990099cf274f8cb0d96c139345cf0bf328affd6 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/feature/configurations/TreeConfiguration.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/feature/configurations/TreeConfiguration.java
@@ -18,13 +18,13 @@ public class TreeConfiguration implements FeatureConfiguration {
return treeConfiguration.trunkProvider;
}), TrunkPlacer.CODEC.fieldOf("trunk_placer").forGetter((treeConfiguration) -> {
return treeConfiguration.trunkPlacer;
- }), BlockStateProvider.CODEC.fieldOf("foliage_provider").forGetter((treeConfiguration) -> {
+ }), net.minecraft.server.MCUtil.fieldWithFallbacks(BlockStateProvider.CODEC, "foliage_provider", "leaves_provider").forGetter((treeConfiguration) -> { // Paper - provide fallback for rename
return treeConfiguration.foliageProvider;
- }), BlockStateProvider.CODEC.fieldOf("sapling_provider").forGetter((treeConfiguration) -> {
+ }), BlockStateProvider.CODEC.optionalFieldOf("sapling_provider", new SimpleStateProvider(Blocks.OAK_SAPLING.defaultBlockState())).forGetter((treeConfiguration) -> { // Paper - provide default - it looks like for now this is OK because it's just used to check canSurvive. Same check happens in 1.16.5 for the default we provide - so it should retain behavior...
return treeConfiguration.saplingProvider;
}), FoliagePlacer.CODEC.fieldOf("foliage_placer").forGetter((treeConfiguration) -> {
return treeConfiguration.foliagePlacer;
- }), BlockStateProvider.CODEC.fieldOf("dirt_provider").forGetter((treeConfiguration) -> {
+ }), BlockStateProvider.CODEC.optionalFieldOf("dirt_provider", new SimpleStateProvider(Blocks.DIRT.defaultBlockState())).forGetter((treeConfiguration) -> { // Paper - provide defaults, old data DOES NOT have this key (thankfully ALL OLD DATA used DIRT)
return treeConfiguration.dirtProvider;
}), FeatureSize.CODEC.fieldOf("minimum_size").forGetter((treeConfiguration) -> {
return treeConfiguration.minimumSize;

View file

@ -1,325 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Thu, 9 Jul 2020 13:34:59 -0700
Subject: [PATCH] Optimise WorldServer#notify
Iterating over all of the navigators in the world is pretty expensive.
Instead, only iterate over navigators in the current region that are
eligible for repathing.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index bef7d5b4c8b99d2fbcd975127b16653e0f391338..19a853ceeded1c8803d182d035f0362abfa29933 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -290,15 +290,81 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public final io.papermc.paper.chunk.SingleThreadChunkRegionManager dataRegionManager;
public static final class DataRegionData implements io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionData {
+ // Paper start - optimise notify()
+ private io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Mob> navigators;
+
+ public io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Mob> getNavigators() {
+ return this.navigators;
+ }
+
+ public boolean addToNavigators(final Mob navigator) {
+ if (this.navigators == null) {
+ this.navigators = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>();
+ }
+ return this.navigators.add(navigator);
+ }
+
+ public boolean removeFromNavigators(final Mob navigator) {
+ if (this.navigators == null) {
+ return false;
+ }
+ return this.navigators.remove(navigator);
+ }
+ // Paper end - optimise notify()
}
public static final class DataRegionSectionData implements io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSectionData {
+ // Paper start - optimise notify()
+ private io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Mob> navigators;
+
+ public io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Mob> getNavigators() {
+ return this.navigators;
+ }
+
+ public boolean addToNavigators(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section, final Mob navigator) {
+ if (this.navigators == null) {
+ this.navigators = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>();
+ }
+ final boolean ret = this.navigators.add(navigator);
+ if (ret) {
+ final DataRegionData data = (DataRegionData)section.getRegion().regionData;
+ if (!data.addToNavigators(navigator)) {
+ throw new IllegalStateException();
+ }
+ }
+ return ret;
+ }
+
+ public boolean removeFromNavigators(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section, final Mob navigator) {
+ if (this.navigators == null) {
+ return false;
+ }
+ final boolean ret = this.navigators.remove(navigator);
+ if (ret) {
+ final DataRegionData data = (DataRegionData)section.getRegion().regionData;
+ if (!data.removeFromNavigators(navigator)) {
+ throw new IllegalStateException();
+ }
+ }
+ return ret;
+ }
+ // Paper end - optimise notify()
+
@Override
public void removeFromRegion(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section,
final io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region from) {
final DataRegionSectionData sectionData = (DataRegionSectionData)section.sectionData;
final DataRegionData fromData = (DataRegionData)from.regionData;
+ // Paper start - optimise notify()
+ if (sectionData.navigators != null) {
+ for (final Iterator<Mob> iterator = sectionData.navigators.unsafeIterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) {
+ if (!fromData.removeFromNavigators(iterator.next())) {
+ throw new IllegalStateException();
+ }
+ }
+ }
+ // Paper end - optimise notify()
}
@Override
@@ -308,6 +374,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final DataRegionSectionData sectionData = (DataRegionSectionData)section.sectionData;
final DataRegionData oldRegionData = oldRegion == null ? null : (DataRegionData)oldRegion.regionData;
final DataRegionData newRegionData = (DataRegionData)newRegion.regionData;
+ // Paper start - optimise notify()
+ if (sectionData.navigators != null) {
+ for (final Iterator<Mob> iterator = sectionData.navigators.unsafeIterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) {
+ if (!newRegionData.addToNavigators(iterator.next())) {
+ throw new IllegalStateException();
+ }
+ }
+ }
+ // Paper end - optimise notify()
}
}
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 89a8138e97ab6a399cfbc69cab0ecaa70bb2fe8d..c01b5611fe6946a24fe21eac6a80e3ddadf9f3c1 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1079,6 +1079,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
public void tickNonPassenger(Entity entity) {
// Paper start - log detailed entity tick information
io.papermc.paper.util.TickThread.ensureTickThread("Cannot tick an entity off-main");
+ this.entityManager.updateNavigatorsInRegion(entity); // Paper - optimise notify
try {
if (currentlyTickingEntity.get() == null) {
currentlyTickingEntity.lazySet(entity);
@@ -1524,9 +1525,19 @@ public class ServerLevel extends Level implements WorldGenLevel {
VoxelShape voxelshape1 = newState.getCollisionShape(this, pos);
if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) {
- Iterator iterator = this.navigatingMobs.iterator();
+ // Paper start - optimise notify()
+ io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region region = this.getChunkSource().chunkMap.dataRegionManager.getRegion(pos.getX() >> 4, pos.getZ() >> 4);
+ if (region == null) {
+ return;
+ }
+ io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Mob> navigatorsFromRegion = ((ChunkMap.DataRegionData)region.regionData).getNavigators();
+ if (navigatorsFromRegion == null) {
+ return;
+ }
+ io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<Mob> iterator = navigatorsFromRegion.iterator();
- while (iterator.hasNext()) {
+
+ try { while (iterator.hasNext()) { // Paper end - optimise notify()
// CraftBukkit start - fix SPIGOT-6362
Mob entityinsentient;
try {
@@ -1545,6 +1556,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
navigationabstract.recomputePath(pos);
}
}
+ // Paper start - optimise notify()
+ } finally {
+ iterator.finishedIterating();
+ }
+ // Paper end - optimise notify()
}
} // Paper
@@ -2324,10 +2340,12 @@ public class ServerLevel extends Level implements WorldGenLevel {
public void onTickingStart(Entity entity) {
ServerLevel.this.entityTickList.add(entity);
+ ServerLevel.this.entityManager.addNavigatorsIfPathingToRegion(entity); // Paper - optimise notify
}
public void onTickingEnd(Entity entity) {
ServerLevel.this.entityTickList.remove(entity);
+ ServerLevel.this.entityManager.removeNavigatorsFromData(entity); // Paper - optimise notify
}
public void onTrackingStart(Entity entity) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
index 61080352ef305a1f276dbc297aa680b3175a5da2..10505f32b71b723ed8dbfd9e1348a1691c9f7f18 100644
--- a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
@@ -27,7 +27,7 @@ import net.minecraft.world.phys.Vec3;
public abstract class PathNavigation {
private static final int MAX_TIME_RECOMPUTE = 20;
- protected final Mob mob;
+ protected final Mob mob; public final Mob getEntity() { return this.mob; } // Paper - public accessor
protected final Level level;
@Nullable
protected Path path;
@@ -40,7 +40,7 @@ public abstract class PathNavigation {
protected long lastTimeoutCheck;
protected double timeoutLimit;
protected float maxDistanceToWaypoint = 0.5F;
- protected boolean hasDelayedRecomputation;
+ protected boolean hasDelayedRecomputation; protected final boolean needsPathRecalculation() { return this.hasDelayedRecomputation; } // Paper - public accessor
protected long timeLastRecompute;
protected NodeEvaluator nodeEvaluator;
private BlockPos targetPos;
@@ -49,6 +49,13 @@ public abstract class PathNavigation {
public final PathFinder pathFinder;
private boolean isStuck;
+ // Paper start
+ public boolean isViableForPathRecalculationChecking() {
+ return !this.needsPathRecalculation() &&
+ (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0);
+ }
+ // Paper end
+
public PathNavigation(Mob mob, Level world) {
this.mob = mob;
this.level = world;
@@ -405,7 +412,7 @@ public abstract class PathNavigation {
}
public void recomputePath(BlockPos pos) {
- if (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0) {
+ if (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0) { // Paper - diff on change - needed for isViableForPathRecalculationChecking()
Node node = this.path.getEndNode();
Vec3 vec3 = new Vec3(((double)node.x + this.mob.getX()) / 2.0D, ((double)node.y + this.mob.getY()) / 2.0D, ((double)node.z + this.mob.getZ()) / 2.0D);
if (pos.closerThan(vec3, (double)(this.path.getNodeCount() - this.path.getNextNodeIndex()))) {
diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
index 976d206a17add01a31ae38b966913368cf386cb1..28c1f144f2cc8675ed61dc814456859309970480 100644
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
@@ -71,6 +71,65 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
// CraftBukkit end
+ // Paper start - optimise notify()
+ public final void removeNavigatorsFromData(Entity entity, final int chunkX, final int chunkZ) {
+ if (!(entity instanceof net.minecraft.world.entity.Mob)) {
+ return;
+ }
+ io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section =
+ this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.getRegionSection(chunkX, chunkZ);
+ if (section != null) {
+ net.minecraft.server.level.ChunkMap.DataRegionSectionData sectionData = (net.minecraft.server.level.ChunkMap.DataRegionSectionData)section.sectionData;
+ sectionData.removeFromNavigators(section, ((net.minecraft.world.entity.Mob)entity));
+ }
+ }
+
+ public final void removeNavigatorsFromData(Entity entity) {
+ if (!(entity instanceof net.minecraft.world.entity.Mob)) {
+ return;
+ }
+ BlockPos entityPos = entity.blockPosition();
+ io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section =
+ this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.getRegionSection(entityPos.getX() >> 4, entityPos.getZ() >> 4);
+ if (section != null) {
+ net.minecraft.server.level.ChunkMap.DataRegionSectionData sectionData = (net.minecraft.server.level.ChunkMap.DataRegionSectionData)section.sectionData;
+ sectionData.removeFromNavigators(section, ((net.minecraft.world.entity.Mob)entity));
+ }
+ }
+
+ public final void addNavigatorsIfPathingToRegion(Entity entity) {
+ if (!(entity instanceof net.minecraft.world.entity.Mob)) {
+ return;
+ }
+ BlockPos entityPos = entity.blockPosition();
+ io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section =
+ this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.getRegionSection(entityPos.getX() >> 4, entityPos.getZ() >> 4);
+ if (section != null) {
+ net.minecraft.server.level.ChunkMap.DataRegionSectionData sectionData = (net.minecraft.server.level.ChunkMap.DataRegionSectionData)section.sectionData;
+ if (((net.minecraft.world.entity.Mob)entity).getNavigation().isViableForPathRecalculationChecking()) {
+ sectionData.addToNavigators(section, ((net.minecraft.world.entity.Mob)entity));
+ }
+ }
+ }
+
+ public final void updateNavigatorsInRegion(Entity entity) {
+ if (!(entity instanceof net.minecraft.world.entity.Mob)) {
+ return;
+ }
+ BlockPos entityPos = entity.blockPosition();
+ io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section =
+ this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.getRegionSection(entityPos.getX() >> 4, entityPos.getZ() >> 4);
+ if (section != null) {
+ net.minecraft.server.level.ChunkMap.DataRegionSectionData sectionData = (net.minecraft.server.level.ChunkMap.DataRegionSectionData)section.sectionData;
+ if (((net.minecraft.world.entity.Mob)entity).getNavigation().isViableForPathRecalculationChecking()) {
+ sectionData.addToNavigators(section, ((net.minecraft.world.entity.Mob)entity));
+ } else {
+ sectionData.removeFromNavigators(section, ((net.minecraft.world.entity.Mob)entity));
+ }
+ }
+ }
+ // Paper end - optimise notify()
+
void removeSectionIfEmpty(long sectionPos, EntitySection<T> section) {
if (section.isEmpty()) {
this.sectionStorage.remove(sectionPos);
@@ -462,11 +521,25 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
@Override
public void onMove() {
BlockPos blockposition = this.entity.blockPosition();
- long i = SectionPos.asLong(blockposition);
+ long i = SectionPos.asLong(blockposition); final long newSectionPos = i; // Paper - diff on change, new position section
if (i != this.currentSectionKey) {
PersistentEntitySectionManager.this.entitySliceManager.moveEntity((Entity)this.entity); // Paper
- Visibility visibility = this.currentSection.getStatus();
+ Visibility visibility = this.currentSection.getStatus(); final Visibility oldVisibility = visibility; // Paper - diff on change - this should be OLD section visibility
+ // Paper start
+ int shift = PersistentEntitySectionManager.this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.regionChunkShift;
+ int oldChunkX = io.papermc.paper.util.CoordinateUtils.getChunkSectionX(this.currentSectionKey);
+ int oldChunkZ = io.papermc.paper.util.CoordinateUtils.getChunkSectionZ(this.currentSectionKey);
+ int oldRegionX = oldChunkX >> shift;
+ int oldRegionZ = oldChunkZ >> shift;
+
+ int newRegionX = io.papermc.paper.util.CoordinateUtils.getChunkSectionX(newSectionPos) >> shift;
+ int newRegionZ = io.papermc.paper.util.CoordinateUtils.getChunkSectionZ(newSectionPos) >> shift;
+
+ if (oldRegionX != newRegionX || oldRegionZ != newRegionZ) {
+ PersistentEntitySectionManager.this.removeNavigatorsFromData((Entity)this.entity, oldChunkX, oldChunkZ);
+ }
+ // Paper end
if (!this.currentSection.remove(this.entity)) {
PersistentEntitySectionManager.LOGGER.warn("Entity {} wasn't found in section {} (moving to {})", this.entity, SectionPos.of(this.currentSectionKey), i);
@@ -478,6 +551,11 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
entitysection.add(this.entity); // CraftBukkit - decompile error
this.currentSection = entitysection;
this.currentSectionKey = i;
+ // Paper start
+ if ((oldRegionX != newRegionX || oldRegionZ != newRegionZ) && oldVisibility.isTicking() && entitysection.getStatus().isTicking()) {
+ PersistentEntitySectionManager.this.addNavigatorsIfPathingToRegion((Entity)this.entity);
+ }
+ // Paper end
this.updateStatus(visibility, entitysection.getStatus());
}

View file

@ -1,327 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Thu, 27 Aug 2020 20:51:40 -0700
Subject: [PATCH] Remove streams for villager AI
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
index 09998d160a6d79fdb5a5041a5d572649a1532e6a..84bd9298840ba60abbdb0675cda0458e0d6a534a 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
@@ -30,11 +30,19 @@ public class GateBehavior<E extends LivingEntity> extends Behavior<E> {
@Override
protected boolean canStillUse(ServerLevel world, E entity, long time) {
- return this.behaviors.stream().filter((behavior) -> {
- return behavior.getStatus() == Behavior.Status.RUNNING;
- }).anyMatch((behavior) -> {
- return behavior.canStillUse(world, entity, time);
- });
+ // Paper start - remove streams
+ List<ShufflingList.WeightedEntry<Behavior<? super E>>> entries = this.behaviors.entries;
+ for (int i = 0; i < entries.size(); i++) {
+ ShufflingList.WeightedEntry<Behavior<? super E>> entry = entries.get(i);
+ Behavior<? super E> behavior = entry.getData();
+ if (behavior.getStatus() == Status.RUNNING) {
+ if (behavior.canStillUse(world, entity, time)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ // Paper end - remove streams
}
@Override
@@ -45,25 +53,35 @@ public class GateBehavior<E extends LivingEntity> extends Behavior<E> {
@Override
protected void start(ServerLevel world, E entity, long time) {
this.orderPolicy.apply(this.behaviors);
- this.runningPolicy.apply(this.behaviors.stream(), world, entity, time);
+ this.runningPolicy.apply(this.behaviors.entries, world, entity, time); // Paper - remove streams
}
@Override
protected void tick(ServerLevel world, E entity, long time) {
- this.behaviors.stream().filter((behavior) -> {
- return behavior.getStatus() == Behavior.Status.RUNNING;
- }).forEach((behavior) -> {
- behavior.tickOrStop(world, entity, time);
- });
+ // Paper start - remove streams
+ List<ShufflingList.WeightedEntry<Behavior<? super E>>> entries = this.behaviors.entries;
+ for (int i = 0; i < entries.size(); i++) {
+ ShufflingList.WeightedEntry<Behavior<? super E>> entry = entries.get(i);
+ Behavior<? super E> behavior = entry.getData();
+ if (behavior.getStatus() == Status.RUNNING) {
+ behavior.tickOrStop(world, entity, time);
+ }
+ }
+ // Paper end - remove streams
}
@Override
protected void stop(ServerLevel world, E entity, long time) {
- this.behaviors.stream().filter((behavior) -> {
- return behavior.getStatus() == Behavior.Status.RUNNING;
- }).forEach((behavior) -> {
- behavior.doStop(world, entity, time);
- });
+ // Paper start - remove streams
+ List<ShufflingList.WeightedEntry<Behavior<? super E>>> entries = this.behaviors.entries;
+ for (int i = 0; i < entries.size(); i++) {
+ ShufflingList.WeightedEntry<Behavior<? super E>> entry = entries.get(i);
+ Behavior<? super E> behavior = entry.getData();
+ if (behavior.getStatus() == Status.RUNNING) {
+ behavior.doStop(world, entity, time);
+ }
+ }
+ // Paper end - remove streams
this.exitErasedMemories.forEach(entity.getBrain()::eraseMemory);
}
@@ -94,25 +112,33 @@ public class GateBehavior<E extends LivingEntity> extends Behavior<E> {
public static enum RunningPolicy {
RUN_ONE {
@Override
- public <E extends LivingEntity> void apply(Stream<Behavior<? super E>> tasks, ServerLevel world, E entity, long time) {
- tasks.filter((behavior) -> {
- return behavior.getStatus() == Behavior.Status.STOPPED;
- }).filter((behavior) -> {
- return behavior.tryStart(world, entity, time);
- }).findFirst();
+ // Paper start - remove streams
+ public <E extends LivingEntity> void apply(List<ShufflingList.WeightedEntry<Behavior<? super E>>> tasks, ServerLevel world, E entity, long time) {
+ for (int i = 0; i < tasks.size(); i++) {
+ ShufflingList.WeightedEntry<Behavior<? super E>> task = tasks.get(i);
+ Behavior<? super E> behavior = task.getData();
+ if (behavior.getStatus() == Status.STOPPED && behavior.tryStart(world, entity, time)) {
+ break;
+ }
+ }
+ // Paper end - remove streams
}
},
TRY_ALL {
@Override
- public <E extends LivingEntity> void apply(Stream<Behavior<? super E>> tasks, ServerLevel world, E entity, long time) {
- tasks.filter((behavior) -> {
- return behavior.getStatus() == Behavior.Status.STOPPED;
- }).forEach((behavior) -> {
- behavior.tryStart(world, entity, time);
- });
+ // Paper start - remove streams
+ public <E extends LivingEntity> void apply(List<ShufflingList.WeightedEntry<Behavior<? super E>>> tasks, ServerLevel world, E entity, long time) {
+ for (int i = 0; i < tasks.size(); i++) {
+ ShufflingList.WeightedEntry<Behavior<? super E>> task = tasks.get(i);
+ Behavior<? super E> behavior = task.getData();
+ if (behavior.getStatus() == Status.STOPPED) {
+ behavior.tryStart(world, entity, time);
+ }
+ }
+ // Paper end - remove streams
}
};
- public abstract <E extends LivingEntity> void apply(Stream<Behavior<? super E>> tasks, ServerLevel world, E entity, long time);
+ public abstract <E extends LivingEntity> void apply(List<ShufflingList.WeightedEntry<Behavior<? super E>>> tasks, ServerLevel world, E entity, long time); // Paper - remove streams
}
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java b/src/main/java/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java
index 1f59e790d62f0be8e505e339a6699ca3964aea0d..ee783bb7cbec2f58549cb95fde7cbc4c47efa1cb 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java
@@ -34,21 +34,42 @@ public class SetLookAndInteract extends Behavior<LivingEntity> {
@Override
public boolean checkExtraStartConditions(ServerLevel world, LivingEntity entity) {
- return this.selfFilter.test(entity) && this.getVisibleEntities(entity).stream().anyMatch(this::isMatchingTarget);
+ // Paper start - remove streams
+ if (!this.selfFilter.test(entity)) {
+ return false;
+ }
+
+ List<LivingEntity> visibleEntities = this.getVisibleEntities(entity);
+ for (int i = 0; i < visibleEntities.size(); i++) {
+ LivingEntity livingEntity = visibleEntities.get(i);
+ if (this.isMatchingTarget(livingEntity)) {
+ return true;
+ }
+ }
+ return false;
+ // Paper end - remove streams
}
@Override
public void start(ServerLevel world, LivingEntity entity, long time) {
super.start(world, entity, time);
Brain<?> brain = entity.getBrain();
- brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES).ifPresent((list) -> {
- list.stream().filter((livingEntity2) -> {
- return livingEntity2.distanceToSqr(entity) <= (double)this.interactionRangeSqr;
- }).filter(this::isMatchingTarget).findFirst().ifPresent((livingEntity) -> {
- brain.setMemory(MemoryModuleType.INTERACTION_TARGET, livingEntity);
- brain.setMemory(MemoryModuleType.LOOK_TARGET, new EntityTracker(livingEntity, true));
- });
- });
+ // Paper start - remove streams
+ List<LivingEntity> list = brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES).orElse(null);
+ if (list != null) {
+ double maxRangeSquared = (double)this.interactionRangeSqr;
+ for (int i = 0; i < list.size(); i++) {
+ LivingEntity livingEntity2 = list.get(i);
+ if (livingEntity2.distanceToSqr(entity) <= maxRangeSquared) {
+ if (this.isMatchingTarget(livingEntity2)) {
+ brain.setMemory(MemoryModuleType.INTERACTION_TARGET, livingEntity2);
+ brain.setMemory(MemoryModuleType.LOOK_TARGET, new EntityTracker(livingEntity2, true));
+ break;
+ }
+ }
+ }
+ }
+ // Paper end - remove streams
}
private boolean isMatchingTarget(LivingEntity entity) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
index 4fa64b1e2004810906bb0b174436c8e687a75ada..aaff4038867820ab2694f036dcd3c419657be6b8 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
@@ -12,7 +12,7 @@ import java.util.Random;
import java.util.stream.Stream;
public class ShufflingList<U> {
- protected final List<ShufflingList.WeightedEntry<U>> entries;
+ public final List<ShufflingList.WeightedEntry<U>> entries; // Paper - public
private final Random random = new Random();
private final boolean isUnsafe; // Paper
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
index 49f3b25d28072b61f5cc97260df61df892a58714..71f2692c83feafbb31f45427e6c738cb3881c82c 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
@@ -25,17 +25,20 @@ public class NearestItemSensor extends Sensor<Mob> {
protected void doTick(ServerLevel world, Mob entity) {
Brain<?> brain = entity.getBrain();
List<ItemEntity> list = world.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(8.0D, 4.0D, 8.0D), (itemEntity) -> {
- return true;
+ return itemEntity.closerThan(entity, 9.0D) && entity.wantsToPickUp(itemEntity.getItem()); // Paper - move predicate into getEntities
});
- list.sort(Comparator.comparingDouble(entity::distanceToSqr));
+ list.sort((e1, e2) -> Double.compare(entity.distanceToSqr(e1), entity.distanceToSqr(e2))); // better to take the sort perf hit than using line of sight more than we need to.
+ // Paper start - remove streams
// Paper start - remove streams in favour of lists
ItemEntity nearest = null;
- for (ItemEntity entityItem : list) {
- if (entity.wantsToPickUp(entityItem.getItem()) && entityItem.closerThan(entity, 9.0D) && entity.hasLineOfSight(entityItem)) {
+ for (int i = 0; i < list.size(); i++) {
+ ItemEntity entityItem = list.get(i);
+ if (entity.hasLineOfSight(entityItem)) {
nearest = entityItem;
break;
}
}
+ // Paper end - remove streams
brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, Optional.ofNullable(nearest));
// Paper end
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
index ffd83db0a419ab589e89feeddd3fb038d6ed5839..c6947aa93b7d2fbc23b0c0e76eed061eb03140c7 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
@@ -18,12 +18,19 @@ public class NearestLivingEntitySensor extends Sensor<LivingEntity> {
List<LivingEntity> list = world.getEntitiesOfClass(LivingEntity.class, aABB, (livingEntity2) -> {
return livingEntity2 != entity && livingEntity2.isAlive();
});
- list.sort(Comparator.comparingDouble(entity::distanceToSqr));
+ // Paper start - remove streams
+ list.sort((e1, e2) -> Double.compare(entity.distanceToSqr(e1), entity.distanceToSqr(e2)));
Brain<?> brain = entity.getBrain();
brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, list);
// Paper start - remove streams in favour of lists
- List<LivingEntity> visibleMobs = new java.util.ArrayList<>(list);
- visibleMobs.removeIf(otherEntityLiving -> !Sensor.isEntityTargetable(entity, otherEntityLiving));
+ List<LivingEntity> visibleMobs = new java.util.ArrayList<>();
+ for (int i = 0, len = list.size(); i < len; i++) {
+ LivingEntity nearby = list.get(i);
+ if (Sensor.isEntityTargetable(entity, nearby)) {
+ visibleMobs.add(nearby);
+ }
+ }
+ // Paper end - remove streams
brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, visibleMobs);
// Paper end
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
index 457ea75137b8b02dc32bf1769ae8d57c470da470..217c8fd1edf664dc568ee0559a38e0bd2a36696c 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
@@ -21,25 +21,31 @@ public class PlayerSensor extends Sensor<LivingEntity> {
@Override
protected void doTick(ServerLevel world, LivingEntity entity) {
- // Paper start - remove streams in favour of lists
- List<Player> players = new java.util.ArrayList<>(world.players());
- players.removeIf(player -> !EntitySelector.NO_SPECTATORS.test(player) || !entity.closerThan(player, 16.0D)); // Paper - removeIf only re-allocates once compared to iterator
+ // Paper start - remove streams
+ List<Player> players = (List)world.getNearbyPlayers(entity, entity.getX(), entity.getY(), entity.getZ(), 16.0D, EntitySelector.NO_SPECTATORS);
+ players.sort((e1, e2) -> Double.compare(entity.distanceToSqr(e1), entity.distanceToSqr(e2)));
Brain<?> brain = entity.getBrain();
brain.setMemory(MemoryModuleType.NEAREST_PLAYERS, players);
- Player nearest = null, nearestTargetable = null;
- for (Player player : players) {
- if (Sensor.isEntityTargetable(entity, player)) {
- if (nearest == null) nearest = player;
- if (Sensor.isEntityAttackable(entity, player)) {
- nearestTargetable = player;
- break; // Both variables are assigned, no reason to loop further
- }
+ Player firstTargetable = null;
+ Player firstAttackable = null;
+ for (int index = 0, len = players.size(); index < len; ++index) {
+ Player player = players.get(index);
+ if (firstTargetable == null && isEntityTargetable(entity, player)) {
+ firstTargetable = player;
+ }
+ if (firstAttackable == null && isEntityAttackable(entity, player)) {
+ firstAttackable = player;
+ }
+
+ if (firstAttackable != null && firstTargetable != null) {
+ break;
}
}
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, nearest);
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, nearestTargetable);
- // Paper end
+
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, firstTargetable);
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, Optional.ofNullable(firstAttackable));
+ // Paper end - remove streams
}
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java
index 478010bc291fa3276aab0f66ce6283403af710ec..de39b608856bdf9bef7120a6922c01c5745f3771 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java
@@ -22,7 +22,17 @@ public class VillagerBabiesSensor extends Sensor<LivingEntity> {
}
private List<LivingEntity> getNearestVillagerBabies(LivingEntity entities) {
- return this.getVisibleEntities(entities).stream().filter(this::isVillagerBaby).collect(Collectors.toList());
+ // Paper start - remove streams
+ List<LivingEntity> list = new java.util.ArrayList<>();
+ List<LivingEntity> visibleEntities = this.getVisibleEntities(entities);
+ for (int i = 0; i < visibleEntities.size(); i++) {
+ LivingEntity livingEntity = visibleEntities.get(i);
+ if (this.isVillagerBaby(livingEntity)) {
+ list.add(livingEntity);
+ }
+ }
+ return list;
+ // Paper end - remove streams
}
private boolean isVillagerBaby(LivingEntity entity) {

File diff suppressed because one or more lines are too long