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:
parent
f04f3321e3
commit
b39fa92d5d
50 changed files with 2250 additions and 382 deletions
|
@ -6,23 +6,29 @@ Subject: [PATCH] Guard against serializing mismatching chunk coordinate
|
|||
Should help if something dumb happens
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
index 992a7d6c4372942711a1381ac08ee13910a62cb0..37855d2183212cb043b30418740c0fd047dbc07f 100644
|
||||
index 384ee6fbc65baff381d875665fd2462dbc99683e..980c784b8e5365b62cbeef7f32af9f4383cc01e6 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
@@ -75,6 +75,12 @@ public class ChunkSerializer {
|
||||
@@ -75,6 +75,18 @@ public class ChunkSerializer {
|
||||
|
||||
public ChunkSerializer() {}
|
||||
|
||||
+ // Paper start - guard against serializing mismatching coordinates
|
||||
+ // TODO Note: This needs to be re-checked each update
|
||||
+ public static ChunkPos getChunkCoordinate(CompoundTag chunkData) {
|
||||
+ return new ChunkPos(chunkData.getInt("xPos"), chunkData.getInt("zPos"));
|
||||
+ final int dataVersion = ChunkStorage.getVersion(chunkData);
|
||||
+ if (dataVersion < 2842) { // Level tag is removed after this version
|
||||
+ final CompoundTag levelData = chunkData.getCompound("Level");
|
||||
+ return new ChunkPos(levelData.getInt("xPos"), levelData.getInt("zPos"));
|
||||
+ } else {
|
||||
+ return new ChunkPos(chunkData.getInt("xPos"), chunkData.getInt("zPos"));
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
// Paper start
|
||||
public static final class InProgressChunkHolder {
|
||||
|
||||
@@ -100,7 +106,7 @@ public class ChunkSerializer {
|
||||
@@ -100,7 +112,7 @@ public class ChunkSerializer {
|
||||
public static InProgressChunkHolder loadChunk(ServerLevel world, PoiManager poiStorage, ChunkPos chunkPos, CompoundTag nbt, boolean distinguish) {
|
||||
java.util.ArrayDeque<Runnable> tasksToExecuteOnMain = new java.util.ArrayDeque<>();
|
||||
// Paper end
|
||||
|
@ -32,7 +38,7 @@ index 992a7d6c4372942711a1381ac08ee13910a62cb0..37855d2183212cb043b30418740c0fd0
|
|||
if (!Objects.equals(chunkPos, chunkcoordintpair1)) {
|
||||
ChunkSerializer.LOGGER.error("Chunk file at {} is in the wrong location; relocating. (Expected {}, got {})", chunkPos, chunkPos, chunkcoordintpair1);
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
index 1fc202caf9051f12192ed479898b01b0a02eebbd..0631a5b1af38ace5ad167d1986271081bd2ee7a6 100644
|
||||
index 2eba8c2d1e978f677eaedac2e09cd1124f1d03db..a750b40be3ba5ba258ca2540ab0398deac5a6c5e 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
@@ -132,6 +132,13 @@ public class ChunkStorage implements AutoCloseable {
|
||||
|
|
|
@ -256,10 +256,10 @@ index 81701abd11fbc4671393a76a42973f53835ca234..e8cf0088e94925934acd02ba05b9411b
|
|||
|
||||
public String toString() {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
index 0980efbc9ef092f56713c7ef776f78fd89cca818..634e44c3eac516f080b565a3b4c7691e31a3eb38 100644
|
||||
index 980c784b8e5365b62cbeef7f32af9f4383cc01e6..46beea026eec707c69194da6d1d51dc66b61f54e 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
@@ -496,11 +496,11 @@ public class ChunkSerializer {
|
||||
@@ -502,11 +502,11 @@ public class ChunkSerializer {
|
||||
}
|
||||
|
||||
if (nibblearray != null && !nibblearray.isEmpty()) {
|
||||
|
|
|
@ -9,10 +9,10 @@ the game, immediately stop the server to prevent data corruption.
|
|||
You can override this functionality at your own peril.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
index 5c21871c7bdfce191db499860725da769dc9caac..d44ce73ea91abd7199695c417c534b6e4ca34e6a 100644
|
||||
index 46beea026eec707c69194da6d1d51dc66b61f54e..a5bd7d9a7440887d8997a96efebaf3db155263be 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
@@ -103,9 +103,22 @@ public class ChunkSerializer {
|
||||
@@ -109,9 +109,22 @@ public class ChunkSerializer {
|
||||
return holder.protoChunk;
|
||||
}
|
||||
|
||||
|
|
81
patches/server/0727-Add-Raw-Byte-Entity-Serialization.patch
Normal file
81
patches/server/0727-Add-Raw-Byte-Entity-Serialization.patch
Normal file
|
@ -0,0 +1,81 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Mariell Hoversholm <proximyst@proximyst.com>
|
||||
Date: Sun, 24 Oct 2021 16:20:31 -0400
|
||||
Subject: [PATCH] Add Raw Byte Entity Serialization
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index dcfc726ab96dccc05848219e824ad7612dbfbdab..7713f26d4a97df94c27694d28881d298e4c54147 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -1788,6 +1788,15 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - Entity serialization api
|
||||
+ public boolean serializeEntity(CompoundTag compound) {
|
||||
+ List<Entity> pass = new java.util.ArrayList<>(this.getPassengers());
|
||||
+ this.passengers = ImmutableList.of();
|
||||
+ boolean result = save(compound);
|
||||
+ this.passengers = ImmutableList.copyOf(pass);
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end
|
||||
public boolean save(CompoundTag nbt) {
|
||||
return this.isPassenger() ? false : this.saveAsPassenger(nbt);
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
index ee50ea695585639d0ff184b675f3fb3b205b9f86..0bd800e1aeda87689a6c56ee6fadda381c74a4ff 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
@@ -1276,5 +1276,15 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
}
|
||||
return set;
|
||||
}
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean spawnAt(Location location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
|
||||
+ Preconditions.checkNotNull(location, "location cannot be null");
|
||||
+ Preconditions.checkNotNull(reason, "reason cannot be null");
|
||||
+ entity.level = ((CraftWorld) location.getWorld()).getHandle();
|
||||
+ entity.setPos(location.getX(), location.getY(), location.getZ());
|
||||
+ entity.setRot(location.getYaw(), location.getPitch());
|
||||
+ return !entity.valid && entity.level.addEntity(entity, reason);
|
||||
+ }
|
||||
// Paper end
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
||||
index 691b110e8a64081b68868456089908fe38fbc1cf..3eb5e7f7d449ccd862f42e2e131ebcddbdf79afb 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
||||
@@ -421,6 +421,30 @@ public final class CraftMagicNumbers implements UnsafeValues {
|
||||
return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.of((CompoundTag) converted.getValue()));
|
||||
}
|
||||
|
||||
+ @Override
|
||||
+ public byte[] serializeEntity(org.bukkit.entity.Entity entity) {
|
||||
+ Preconditions.checkNotNull(entity, "null cannot be serialized");
|
||||
+ Preconditions.checkArgument(entity instanceof org.bukkit.craftbukkit.entity.CraftEntity, "only CraftEntities can be serialized");
|
||||
+
|
||||
+ CompoundTag compound = new CompoundTag();
|
||||
+ ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().serializeEntity(compound);
|
||||
+ return serializeNbtToBytes(compound);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public org.bukkit.entity.Entity deserializeEntity(byte[] data, org.bukkit.World world, boolean preserveUUID) {
|
||||
+ Preconditions.checkNotNull(data, "null cannot be deserialized");
|
||||
+ Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing");
|
||||
+
|
||||
+ CompoundTag compound = deserializeNbtFromBytes(data);
|
||||
+ int dataVersion = compound.getInt("DataVersion");
|
||||
+ Dynamic<Tag> converted = DataFixers.getDataFixer().update(References.ENTITY_TREE, new Dynamic<>(NbtOps.INSTANCE, compound), dataVersion, getDataVersion());
|
||||
+ compound = (CompoundTag) converted.getValue();
|
||||
+ if (!preserveUUID) compound.remove("UUID"); // Generate a new UUID so we don't have to worry about deserializing the same entity twice
|
||||
+ return net.minecraft.world.entity.EntityType.create(compound, ((org.bukkit.craftbukkit.CraftWorld) world).getHandle())
|
||||
+ .orElseThrow(() -> new IllegalArgumentException("An ID was not found for the data. Did you downgrade?")).getBukkitEntity();
|
||||
+ }
|
||||
+
|
||||
private byte[] serializeNbtToBytes(CompoundTag compound) {
|
||||
compound.putInt("DataVersion", getDataVersion());
|
||||
java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();
|
|
@ -953,7 +953,7 @@ index 5d189257f494eb12b5fd98b12da6dd09ca14f972..913b56361dece6c699ed7fad7e580d40
|
|||
+ // Paper end
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index dcfc726ab96dccc05848219e824ad7612dbfbdab..db6c11694e6316c50a3f0a138e09542fdae45718 100644
|
||||
index 7713f26d4a97df94c27694d28881d298e4c54147..3110f8cbf65ba0fefbf78f90915ee358694d20ca 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -386,6 +386,56 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
|
@ -1013,7 +1013,7 @@ index dcfc726ab96dccc05848219e824ad7612dbfbdab..db6c11694e6316c50a3f0a138e09542f
|
|||
public Entity(EntityType<?> type, Level world) {
|
||||
this.id = Entity.ENTITY_COUNTER.incrementAndGet();
|
||||
this.passengers = ImmutableList.of();
|
||||
@@ -2233,11 +2283,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
@@ -2242,11 +2292,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ hoping that at least then we don't swap chunks, and maybe recover
|
|||
them all.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
index f58050eaa1354ace7b3558d528ab8effdd1432aa..23b3203d04cf1dd2fc3c42e2c7b287a949c81699 100644
|
||||
index a5bd7d9a7440887d8997a96efebaf3db155263be..c1f22e3e9b3ab91f05556707ace46e9c627b025e 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
||||
@@ -66,6 +66,12 @@ import org.apache.logging.log4j.LogManager;
|
||||
|
@ -26,7 +26,7 @@ index f58050eaa1354ace7b3558d528ab8effdd1432aa..23b3203d04cf1dd2fc3c42e2c7b287a9
|
|||
|
||||
public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
@@ -437,7 +443,7 @@ public class ChunkSerializer {
|
||||
@@ -443,7 +449,7 @@ public class ChunkSerializer {
|
||||
nbttagcompound.putInt("xPos", chunkcoordintpair.x);
|
||||
nbttagcompound.putInt("yPos", chunk.getMinSection());
|
||||
nbttagcompound.putInt("zPos", chunkcoordintpair.z);
|
|
@ -123,7 +123,7 @@ index 5d4f20a31ad99b4e808bb9a7aaa2153666af493f..928ac2d5b93b93aa7494374f4f344655
|
|||
|
||||
private void tickPassenger(Entity vehicle, Entity passenger) {
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index db6c11694e6316c50a3f0a138e09542fdae45718..62fe626aa198d19ccfb48f47b8f771b6037cff9c 100644
|
||||
index 3110f8cbf65ba0fefbf78f90915ee358694d20ca..96794dcb87c3606e9d112d4159be8be31ad4329e 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -860,7 +860,42 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
|
@ -183,7 +183,7 @@ index db6c11694e6316c50a3f0a138e09542fdae45718..62fe626aa198d19ccfb48f47b8f771b6
|
|||
}
|
||||
|
||||
protected boolean isHorizontalCollisionMinor(Vec3 adjustedMovement) {
|
||||
@@ -3798,7 +3840,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
@@ -3807,7 +3849,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
}
|
||||
|
||||
public void setDeltaMovement(Vec3 velocity) {
|
||||
|
@ -193,7 +193,7 @@ index db6c11694e6316c50a3f0a138e09542fdae45718..62fe626aa198d19ccfb48f47b8f771b6
|
|||
}
|
||||
|
||||
public void setDeltaMovement(double x, double y, double z) {
|
||||
@@ -3874,7 +3918,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
@@ -3883,7 +3927,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
|
||||
}
|
||||
// Paper end - fix MC-4
|
||||
if (this.position.x != x || this.position.y != y || this.position.z != z) {
|
55
patches/server/0766-Optimise-non-flush-packet-sending.patch
Normal file
55
patches/server/0766-Optimise-non-flush-packet-sending.patch
Normal file
|
@ -0,0 +1,55 @@
|
|||
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 241b086bd096a4bc2175835b2505deda1c143f09..a1aafb037fd340dc93dd2afb758ffc7457d15f84 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;
|
||||
@@ -387,9 +389,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
|
||||
}
|
||||
|
||||
}
|
407
patches/server/0767-Optimise-nearby-player-lookups.patch
Normal file
407
patches/server/0767-Optimise-nearby-player-lookups.patch
Normal file
|
@ -0,0 +1,407 @@
|
|||
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 7418245d5d08706ca2a1378e769abfb0de1076ed..8a7cc96f563c3fb8807d4a8a3249fc0892710d17 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
@@ -94,6 +94,12 @@ public class ChunkHolder {
|
||||
this.setTicketLevel(level);
|
||||
this.changedBlocksPerSection = new ShortSet[world.getSectionsCount()];
|
||||
this.chunkMap = (ChunkMap)playersWatchingChunkProvider; // Paper
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ LevelChunk chunk = this.getFullChunkUnchecked();
|
||||
+ if (chunk != null) {
|
||||
+ chunk.updateGeneralAreaCache();
|
||||
+ }
|
||||
+ // Paper end - optimise checkDespawn
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 925da0baa59f742dbe727c6323cc90b65159f314..3b2db473e5eacbcf55ae8786aff5ac71388a98ee 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -190,21 +190,36 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
final CallbackExecutor chunkLoadConversionCallbackExecutor = new CallbackExecutor(); // Paper
|
||||
// Paper start - distance maps
|
||||
private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>();
|
||||
+ // 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());
|
||||
int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
|
||||
// Note: players need to be explicitly added to distance maps before they can be updated
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ this.playerGeneralAreaMap.add(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS);
|
||||
+ // Paper end - optimise checkDespawn
|
||||
}
|
||||
|
||||
void removePlayerFromDistanceMaps(ServerPlayer player) {
|
||||
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ this.playerGeneralAreaMap.remove(player);
|
||||
+ // Paper end - optimise checkDespawn
|
||||
}
|
||||
|
||||
void updateMaps(ServerPlayer player) {
|
||||
int chunkX = MCUtil.getChunkCoordinate(player.getX());
|
||||
int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
|
||||
// Note: players need to be explicitly added to distance maps before they can be updated
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ this.playerGeneralAreaMap.update(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS);
|
||||
+ // Paper end - optimise checkDespawn
|
||||
}
|
||||
// Paper end
|
||||
// Paper start
|
||||
@@ -280,6 +295,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
this.regionManagers.add(this.dataRegionManager);
|
||||
// Paper end
|
||||
this.playerMobDistanceMap = this.level.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.PlayerMobDistanceMap() : null; // Paper
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ this.playerGeneralAreaMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
|
||||
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
||||
+ LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ);
|
||||
+ if (chunk != null) {
|
||||
+ chunk.updateGeneralAreaCache(newState);
|
||||
+ }
|
||||
+ },
|
||||
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
||||
+ LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ);
|
||||
+ if (chunk != null) {
|
||||
+ chunk.updateGeneralAreaCache(newState);
|
||||
+ }
|
||||
+ });
|
||||
+ // Paper end - optimise checkDespawn
|
||||
}
|
||||
|
||||
protected ChunkGenerator generator() {
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index 5935c442a6534ad51d191a72bc8d2043fa25e2ac..8a219ecabe49d8f2564a365968891b214a090f6d 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -396,6 +396,83 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
return this.getServer().getPlayerList().getPlayer(uuid);
|
||||
}
|
||||
// Paper end
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ public final List<ServerPlayer> playersAffectingSpawning = new java.util.ArrayList<>();
|
||||
+ // Paper end - optimise checkDespawn
|
||||
+ // Paper start - optimise get nearest players for entity AI
|
||||
+ @Override
|
||||
+ public final ServerPlayer getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions condition, @Nullable LivingEntity source,
|
||||
+ double centerX, double centerY, double centerZ) {
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> nearby;
|
||||
+ nearby = this.getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(Mth.floor(centerX) >> 4, Mth.floor(centerZ) >> 4);
|
||||
+
|
||||
+ if (nearby == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ Object[] backingSet = nearby.getBackingSet();
|
||||
+
|
||||
+ double closestDistanceSquared = Double.MAX_VALUE;
|
||||
+ ServerPlayer closest = null;
|
||||
+
|
||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
||||
+ Object _player = backingSet[i];
|
||||
+ if (!(_player instanceof ServerPlayer)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ ServerPlayer player = (ServerPlayer)_player;
|
||||
+
|
||||
+ double distanceSquared = player.distanceToSqr(centerX, centerY, centerZ);
|
||||
+ if (distanceSquared < closestDistanceSquared && condition.test(source, player)) {
|
||||
+ closest = player;
|
||||
+ closestDistanceSquared = distanceSquared;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return closest;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions pathfindertargetcondition, LivingEntity entityliving) {
|
||||
+ return this.getNearestPlayer(pathfindertargetcondition, entityliving, entityliving.getX(), entityliving.getY(), entityliving.getZ());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions pathfindertargetcondition,
|
||||
+ double d0, double d1, double d2) {
|
||||
+ return this.getNearestPlayer(pathfindertargetcondition, null, d0, d1, d2);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public List<Player> getNearbyPlayers(net.minecraft.world.entity.ai.targeting.TargetingConditions condition, LivingEntity source, AABB axisalignedbb) {
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> nearby;
|
||||
+ double centerX = (axisalignedbb.maxX + axisalignedbb.minX) * 0.5;
|
||||
+ double centerZ = (axisalignedbb.maxZ + axisalignedbb.minZ) * 0.5;
|
||||
+ nearby = this.getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(Mth.floor(centerX) >> 4, Mth.floor(centerZ) >> 4);
|
||||
+
|
||||
+ List<Player> ret = new java.util.ArrayList<>();
|
||||
+
|
||||
+ if (nearby == null) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ Object[] backingSet = nearby.getBackingSet();
|
||||
+
|
||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
||||
+ Object _player = backingSet[i];
|
||||
+ if (!(_player instanceof ServerPlayer)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ ServerPlayer player = (ServerPlayer)_player;
|
||||
+
|
||||
+ if (axisalignedbb.contains(player.getX(), player.getY(), player.getZ()) && condition.test(source, player)) {
|
||||
+ ret.add(player);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+ // Paper end - optimise get nearest players for entity AI
|
||||
|
||||
// Add env and gen to constructor, WorldData -> WorldDataServer
|
||||
public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, ServerLevelData iworlddataserver, ResourceKey<Level> resourcekey, DimensionType dimensionmanager, ChunkProgressListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List<CustomSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
|
||||
@@ -484,6 +561,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 697c2663c4deeb8f2ad603c979ab0884ac027930..0b46066d35d9bb38d98a9d6e5ca8dbdc0ba1dc5a 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Mob.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
|
||||
@@ -792,7 +792,12 @@ public abstract class Mob extends LivingEntity {
|
||||
if (this.level.getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) {
|
||||
this.discard();
|
||||
} else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) {
|
||||
- Player entityhuman = this.level.findNearbyPlayer(this, -1.0D, EntitySelector.affectsSpawning); // Paper
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ Player entityhuman = this.level.findNearbyPlayer(this, level.paperConfig.hardDespawnDistances.getInt(this.getType().getCategory()) + 1, EntitySelector.affectsSpawning); // Paper
|
||||
+ if (entityhuman == null) {
|
||||
+ entityhuman = ((ServerLevel)this.level).playersAffectingSpawning.isEmpty() ? null : ((ServerLevel)this.level).playersAffectingSpawning.get(0);
|
||||
+ }
|
||||
+ // Paper end - optimise checkDespawn
|
||||
|
||||
if (entityhuman != null) {
|
||||
double d0 = entityhuman.distanceToSqr((Entity) this);
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index 6be462975523dae9ff436ba1643b2042ec66e554..929f48675a10fdd64cb351305389d680a16963fb 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -243,6 +243,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) {
|
||||
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 63ba93538d990fdd4c9e8c491bb715adc8d57986..e9a37fc6791366ea421f2766a36dc2e014ab7951 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -269,7 +269,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);
|
||||
@@ -342,7 +342,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 b92da719a5d35a60a2e13ccb0f55c41b242f9b50..a9807f9182a4d70e09486612606c7c1d0a6734ac 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -231,6 +231,93 @@ public class LevelChunk extends ChunkAccess {
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
+ // Paper start - optimise checkDespawn
|
||||
+ private boolean playerGeneralAreaCacheSet;
|
||||
+ private com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> playerGeneralAreaCache;
|
||||
+
|
||||
+ public com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> getPlayerGeneralAreaCache() {
|
||||
+ if (!this.playerGeneralAreaCacheSet) {
|
||||
+ this.updateGeneralAreaCache();
|
||||
+ }
|
||||
+ return this.playerGeneralAreaCache;
|
||||
+ }
|
||||
+
|
||||
+ public void updateGeneralAreaCache() {
|
||||
+ this.updateGeneralAreaCache(((ServerLevel)this.level).getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(this.coordinateKey));
|
||||
+ }
|
||||
+
|
||||
+ public void updateGeneralAreaCache(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> value) {
|
||||
+ this.playerGeneralAreaCacheSet = true;
|
||||
+ this.playerGeneralAreaCache = value;
|
||||
+ }
|
||||
+
|
||||
+ public net.minecraft.server.level.ServerPlayer findNearestPlayer(double sourceX, double sourceY, double sourceZ,
|
||||
+ double maxRange, java.util.function.Predicate<Entity> predicate) {
|
||||
+ if (!this.playerGeneralAreaCacheSet) {
|
||||
+ this.updateGeneralAreaCache();
|
||||
+ }
|
||||
+
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> nearby = this.playerGeneralAreaCache;
|
||||
+
|
||||
+ if (nearby == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ Object[] backingSet = nearby.getBackingSet();
|
||||
+ double closestDistance = maxRange < 0.0 ? Double.MAX_VALUE : maxRange * maxRange;
|
||||
+ net.minecraft.server.level.ServerPlayer closest = null;
|
||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
||||
+ Object _player = backingSet[i];
|
||||
+ if (!(_player instanceof net.minecraft.server.level.ServerPlayer)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer)_player;
|
||||
+
|
||||
+ double distance = player.distanceToSqr(sourceX, sourceY, sourceZ);
|
||||
+ if (distance < closestDistance && predicate.test(player)) {
|
||||
+ closest = player;
|
||||
+ closestDistance = distance;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return closest;
|
||||
+ }
|
||||
+
|
||||
+ public void getNearestPlayers(double sourceX, double sourceY, double sourceZ, java.util.function.Predicate<Entity> predicate,
|
||||
+ double range, java.util.List<net.minecraft.server.level.ServerPlayer> ret) {
|
||||
+ if (!this.playerGeneralAreaCacheSet) {
|
||||
+ this.updateGeneralAreaCache();
|
||||
+ }
|
||||
+
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> nearby = this.playerGeneralAreaCache;
|
||||
+
|
||||
+ if (nearby == null) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ double rangeSquared = range * range;
|
||||
+
|
||||
+ Object[] backingSet = nearby.getBackingSet();
|
||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
||||
+ Object _player = backingSet[i];
|
||||
+ if (!(_player instanceof net.minecraft.server.level.ServerPlayer)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer)_player;
|
||||
+
|
||||
+ if (range >= 0.0) {
|
||||
+ double distanceSquared = player.distanceToSqr(sourceX, sourceY, sourceZ);
|
||||
+ if (distanceSquared > rangeSquared) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (predicate == null || predicate.test(player)) {
|
||||
+ ret.add(player);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - optimise checkDespawn
|
||||
|
||||
public LevelChunk(ServerLevel world, ProtoChunk protoChunk, @Nullable LevelChunk.PostLoadProcessor chunk_c) {
|
||||
this(world, protoChunk.getPos(), protoChunk.getUpgradeData(), protoChunk.unpackBlockTicks(), protoChunk.unpackFluidTicks(), protoChunk.getInhabitedTime(), protoChunk.getSections(), chunk_c, protoChunk.getBlendingData());
|
325
patches/server/0768-Optimise-WorldServer-notify.patch
Normal file
325
patches/server/0768-Optimise-WorldServer-notify.patch
Normal file
|
@ -0,0 +1,325 @@
|
|||
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 3b2db473e5eacbcf55ae8786aff5ac71388a98ee..6fc7c1cf269466362dce91fa2cf525e67bee6c15 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -227,15 +227,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
|
||||
@@ -245,6 +311,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 8a219ecabe49d8f2564a365968891b214a090f6d..8837c9793eb25ad88bdb4b0f6198dc7ae353b9b2 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1090,6 +1090,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);
|
||||
@@ -1504,9 +1505,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 {
|
||||
@@ -1525,6 +1536,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
navigationabstract.recomputePath(pos);
|
||||
}
|
||||
}
|
||||
+ // Paper start - optimise notify()
|
||||
+ } finally {
|
||||
+ iterator.finishedIterating();
|
||||
+ }
|
||||
+ // Paper end - optimise notify()
|
||||
|
||||
}
|
||||
} // Paper
|
||||
@@ -2317,10 +2333,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 5884fb42f0880585dee843b98a6ea470a1508e46..4651c2e78089ed28220e767654261c735d2c5eb1 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;
|
||||
@Nullable
|
||||
@@ -50,6 +50,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;
|
||||
@@ -415,7 +422,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 1d61807768dd883cb82bda5d529055bc50e4d1a9..d963243431e1a75f95e673e1268faa7c2320c6b6 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);
|
||||
@@ -451,11 +510,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);
|
||||
@@ -467,6 +540,11 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
entitysection.add(this.entity);
|
||||
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());
|
||||
}
|
||||
|
220
patches/server/0769-Remove-streams-for-villager-AI.patch
Normal file
220
patches/server/0769-Remove-streams-for-villager-AI.patch
Normal file
|
@ -0,0 +1,220 @@
|
|||
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 e644bdd3a6f7c09a44149da03587b796674fa568..c67c448e0d8bdd788b94189651304110694c95da 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((task) -> {
|
||||
- return task.getStatus() == Behavior.Status.RUNNING;
|
||||
- }).anyMatch((task) -> {
|
||||
- return task.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((task) -> {
|
||||
- return task.getStatus() == Behavior.Status.RUNNING;
|
||||
- }).forEach((task) -> {
|
||||
- task.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((task) -> {
|
||||
- return task.getStatus() == Behavior.Status.RUNNING;
|
||||
- }).forEach((task) -> {
|
||||
- task.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((task) -> {
|
||||
- return task.getStatus() == Behavior.Status.STOPPED;
|
||||
- }).filter((task) -> {
|
||||
- return task.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((task) -> {
|
||||
- return task.getStatus() == Behavior.Status.STOPPED;
|
||||
- }).forEach((task) -> {
|
||||
- task.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/ShufflingList.java b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java
|
||||
index 1bc34453933bc7590af45a5559a4fc75eb3e0c5c..204ca4fbd89bdadd902529f1f191df46fce3cace 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/PlayerSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java
|
||||
index 312775d0430f793720211dc29bb293503e799d11..75d9c4f011b5a97def215784c92bb57bbb35d06b 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,30 @@ public class PlayerSensor extends Sensor<LivingEntity> {
|
||||
|
||||
@Override
|
||||
protected void doTick(ServerLevel world, LivingEntity entity) {
|
||||
- List<Player> players = new java.util.ArrayList<>(world.players());
|
||||
- players.removeIf(player -> !EntitySelector.NO_SPECTATORS.test(player) || !entity.closerThan(player, 16.0D));
|
||||
- players.sort(Comparator.comparingDouble(entity::distanceTo));
|
||||
+ // 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
|
||||
}
|
||||
}
|
21636
patches/server/0770-Rewrite-dataconverter-system.patch
Normal file
21636
patches/server/0770-Rewrite-dataconverter-system.patch
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue