mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-30 16:19:03 +01:00
Fix local attribute setting
This commit is contained in:
parent
3ecca26a7b
commit
7e02ed1a60
11 changed files with 22 additions and 1872 deletions
|
@ -2346,10 +2346,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
}
|
}
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
this.language = clientOptions.language();
|
this.language = clientOptions.language();
|
||||||
+ // Paper start
|
+ this.adventure$locale = net.kyori.adventure.translation.Translator.parseLocale(this.language); // Paper
|
||||||
+ this.adventure$locale = net.kyori.adventure.translation.Translator.parseLocale(this.language);
|
|
||||||
+ this.connection.connection.channel.attr(PaperAdventure.LOCALE_ATTRIBUTE).set(this.adventure$locale);
|
|
||||||
+ // Paper end
|
|
||||||
this.requestedViewDistance = clientOptions.viewDistance();
|
this.requestedViewDistance = clientOptions.viewDistance();
|
||||||
this.chatVisibility = clientOptions.chatVisibility();
|
this.chatVisibility = clientOptions.chatVisibility();
|
||||||
this.canChatColor = clientOptions.chatColors();
|
this.canChatColor = clientOptions.chatColors();
|
||||||
|
@ -2418,6 +2415,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
|
|
||||||
this.connection.send(new ClientboundDisconnectPacket(ichatbasecomponent), PacketSendListener.thenRun(() -> {
|
this.connection.send(new ClientboundDisconnectPacket(ichatbasecomponent), PacketSendListener.thenRun(() -> {
|
||||||
|
diff --git a/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
|
||||||
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||||
|
--- a/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
|
||||||
|
+++ b/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
|
||||||
|
@@ -0,0 +0,0 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis
|
||||||
|
@Override
|
||||||
|
public void handleClientInformation(ServerboundClientInformationPacket packet) {
|
||||||
|
this.clientInformation = packet.information();
|
||||||
|
+ this.connection.channel.attr(io.papermc.paper.adventure.PaperAdventure.LOCALE_ATTRIBUTE).set(net.kyori.adventure.translation.Translator.parseLocale(packet.information().language())); // Paper
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||||
|
@ -2502,6 +2511,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
} else {
|
} else {
|
||||||
this.chat(s, message, true);
|
this.chat(s, message, true);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||||
|
public void handleClientInformation(ServerboundClientInformationPacket packet) {
|
||||||
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
||||||
|
this.player.updateOptions(packet.information());
|
||||||
|
+ this.connection.channel.attr(io.papermc.paper.adventure.PaperAdventure.LOCALE_ATTRIBUTE).set(net.kyori.adventure.translation.Translator.parseLocale(packet.information().language())); // Paper
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||||
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||||
|
|
|
@ -20,5 +20,5 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
+ }
|
+ }
|
||||||
+ // Paper end - do not accept invalid information
|
+ // Paper end - do not accept invalid information
|
||||||
this.player.updateOptions(packet.information());
|
this.player.updateOptions(packet.information());
|
||||||
|
this.connection.channel.attr(io.papermc.paper.adventure.PaperAdventure.LOCALE_ATTRIBUTE).set(net.kyori.adventure.translation.Translator.parseLocale(packet.information().language())); // Paper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,150 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
||||||
Date: Sat, 4 Apr 2020 15:27:44 -0700
|
|
||||||
Subject: [PATCH] Allow controlled flushing for network manager
|
|
||||||
|
|
||||||
Only make one flush call when emptying the packet queue too
|
|
||||||
|
|
||||||
This patch will be used to optimise out flush calls in later
|
|
||||||
patches.
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
|
||||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
|
||||||
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
||||||
public ConnectionProtocol protocol;
|
|
||||||
// Paper end
|
|
||||||
|
|
||||||
+ // Paper start - allow controlled flushing
|
|
||||||
+ volatile boolean canFlush = true;
|
|
||||||
+ private final java.util.concurrent.atomic.AtomicInteger packetWrites = new java.util.concurrent.atomic.AtomicInteger();
|
|
||||||
+ private int flushPacketsStart;
|
|
||||||
+ private final Object flushLock = new Object();
|
|
||||||
+
|
|
||||||
+ public void disableAutomaticFlush() {
|
|
||||||
+ synchronized (this.flushLock) {
|
|
||||||
+ this.flushPacketsStart = this.packetWrites.get(); // must be volatile and before canFlush = false
|
|
||||||
+ this.canFlush = false;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public void enableAutomaticFlush() {
|
|
||||||
+ synchronized (this.flushLock) {
|
|
||||||
+ this.canFlush = true;
|
|
||||||
+ if (this.packetWrites.get() != this.flushPacketsStart) { // must be after canFlush = true
|
|
||||||
+ this.flush(); // only make the flush call if we need to
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ private final void flush() {
|
|
||||||
+ if (this.channel.eventLoop().inEventLoop()) {
|
|
||||||
+ this.channel.flush();
|
|
||||||
+ } else {
|
|
||||||
+ this.channel.eventLoop().execute(() -> {
|
|
||||||
+ this.channel.flush();
|
|
||||||
+ });
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ // Paper end - allow controlled flushing
|
|
||||||
+
|
|
||||||
public Connection(PacketFlow side) {
|
|
||||||
this.receiving = side;
|
|
||||||
}
|
|
||||||
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
||||||
io.papermc.paper.util.MCUtil.isMainThread() && packet.isReady() && this.queue.isEmpty() &&
|
|
||||||
(packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty())
|
|
||||||
))) {
|
|
||||||
- this.sendPacket(packet, callbacks);
|
|
||||||
+ this.sendPacket(packet, callbacks, null); // Paper
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// write the packets to the queue, then flush - antixray hooks there already
|
|
||||||
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendPacket(Packet<?> packet, @Nullable PacketSendListener callbacks) {
|
|
||||||
+ // Paper start - add flush parameter
|
|
||||||
+ this.sendPacket(packet, callbacks, Boolean.TRUE);
|
|
||||||
+ }
|
|
||||||
+ private void sendPacket(Packet<?> packet, @Nullable PacketSendListener callbacks, Boolean flushConditional) {
|
|
||||||
+ this.packetWrites.getAndIncrement(); // must be befeore using canFlush
|
|
||||||
+ boolean effectiveFlush = flushConditional == null ? this.canFlush : flushConditional.booleanValue();
|
|
||||||
+ final boolean flush = effectiveFlush || packet instanceof net.minecraft.network.protocol.game.ClientboundKeepAlivePacket || packet instanceof ClientboundDisconnectPacket; // no delay for certain packets
|
|
||||||
+ // Paper end - add flush parameter
|
|
||||||
ConnectionProtocol enumprotocol = ConnectionProtocol.getProtocolForPacket(packet);
|
|
||||||
ConnectionProtocol enumprotocol1 = this.getCurrentProtocol();
|
|
||||||
|
|
||||||
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.channel.eventLoop().inEventLoop()) {
|
|
||||||
- this.doSendPacket(packet, callbacks, enumprotocol, enumprotocol1);
|
|
||||||
+ this.doSendPacket(packet, callbacks, enumprotocol, enumprotocol1, flush); // Paper
|
|
||||||
} else {
|
|
||||||
this.channel.eventLoop().execute(() -> {
|
|
||||||
- this.doSendPacket(packet, callbacks, enumprotocol, enumprotocol1);
|
|
||||||
+ this.doSendPacket(packet, callbacks, enumprotocol, enumprotocol1, flush); // Paper
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doSendPacket(Packet<?> packet, @Nullable PacketSendListener callbacks, ConnectionProtocol packetState, ConnectionProtocol currentState) {
|
|
||||||
+ // Paper start - add flush parameter
|
|
||||||
+ this.doSendPacket(packet, callbacks, packetState, currentState, true);
|
|
||||||
+ }
|
|
||||||
+ private void doSendPacket(Packet<?> packet, @Nullable PacketSendListener callbacks, ConnectionProtocol packetState, ConnectionProtocol currentState, boolean flush) {
|
|
||||||
+ // Paper end - add flush parameter
|
|
||||||
if (packetState != currentState) {
|
|
||||||
this.setProtocol(packetState);
|
|
||||||
}
|
|
||||||
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Paper end
|
|
||||||
- ChannelFuture channelfuture = this.channel.writeAndFlush(packet);
|
|
||||||
+ ChannelFuture channelfuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet); // Paper - add flush parameter
|
|
||||||
|
|
||||||
if (callbacks != null) {
|
|
||||||
channelfuture.addListener((future) -> {
|
|
||||||
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
||||||
private boolean processQueue() {
|
|
||||||
try { // Paper - add pending task queue
|
|
||||||
if (this.queue.isEmpty()) return true;
|
|
||||||
+ // Paper start - make only one flush call per sendPacketQueue() call
|
|
||||||
+ final boolean needsFlush = this.canFlush;
|
|
||||||
+ boolean hasWrotePacket = false;
|
|
||||||
+ // Paper end - make only one flush call per sendPacketQueue() call
|
|
||||||
// If we are on main, we are safe here in that nothing else should be processing queue off main anymore
|
|
||||||
// But if we are not on main due to login/status, the parent is synchronized on packetQueue
|
|
||||||
java.util.Iterator<PacketHolder> iterator = this.queue.iterator();
|
|
||||||
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
||||||
PacketHolder queued = iterator.next(); // poll -> peek
|
|
||||||
|
|
||||||
// Fix NPE (Spigot bug caused by handleDisconnection())
|
|
||||||
- if (queued == null) {
|
|
||||||
+ if (false && queued == null) { // Paper - diff on change, this logic is redundant: iterator guarantees ret of an element - on change, hook the flush logic here
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
||||||
|
|
||||||
Packet<?> packet = queued.packet;
|
|
||||||
if (!packet.isReady()) {
|
|
||||||
+ // Paper start - make only one flush call per sendPacketQueue() call
|
|
||||||
+ if (hasWrotePacket && (needsFlush || this.canFlush)) {
|
|
||||||
+ this.flush();
|
|
||||||
+ }
|
|
||||||
+ // Paper end - make only one flush call per sendPacketQueue() call
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
iterator.remove();
|
|
||||||
if (queued.tryMarkConsumed()) { // Paper - try to mark isConsumed flag for de-duplicating packet
|
|
||||||
- this.sendPacket(packet, queued.listener);
|
|
||||||
+ this.sendPacket(packet, queued.listener, (!iterator.hasNext() && (needsFlush || this.canFlush)) ? Boolean.TRUE : Boolean.FALSE); // Paper - make only one flush call per sendPacketQueue() call
|
|
||||||
+ hasWrotePacket = true; // Paper - make only one flush call per sendPacketQueue() call
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
||||||
Date: Sat, 4 Apr 2020 17:00:20 -0700
|
|
||||||
Subject: [PATCH] Consolidate flush calls for entity tracker packets
|
|
||||||
|
|
||||||
Most server packets seem to be sent from here, so try to avoid
|
|
||||||
expensive flush calls from them.
|
|
||||||
|
|
||||||
This change was motivated due to local testing:
|
|
||||||
|
|
||||||
- My server spawn has 130 cows in it (for testing a prev. patch)
|
|
||||||
- Try to let 200 players join spawn
|
|
||||||
|
|
||||||
Without this change, I could only get 20 players on before they
|
|
||||||
all started timing out due to the load put on the Netty I/O threads.
|
|
||||||
|
|
||||||
With this change I could get all 200 on at 0ms ping.
|
|
||||||
|
|
||||||
(one of the primary issues is that my CPU is kinda trash, and having
|
|
||||||
4 extra threads at 100% is just too much for it).
|
|
||||||
|
|
||||||
So in general this patch should reduce Netty I/O thread load.
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
||||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
|
||||||
this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
|
|
||||||
gameprofilerfiller.pop();
|
|
||||||
// Paper end - use set of chunks requiring updates, rather than iterating every single one loaded
|
|
||||||
+ // Paper start - controlled flush for entity tracker packets
|
|
||||||
+ List<net.minecraft.network.Connection> disabledFlushes = new java.util.ArrayList<>(this.level.players.size());
|
|
||||||
+ for (ServerPlayer player : this.level.players) {
|
|
||||||
+ net.minecraft.server.network.ServerGamePacketListenerImpl connection = player.connection;
|
|
||||||
+ if (connection != null) {
|
|
||||||
+ connection.connection.disableAutomaticFlush();
|
|
||||||
+ disabledFlushes.add(connection.connection);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ try { // Paper end - controlled flush for entity tracker packets
|
|
||||||
this.chunkMap.tick();
|
|
||||||
+ // Paper start - controlled flush for entity tracker packets
|
|
||||||
+ } finally {
|
|
||||||
+ for (net.minecraft.network.Connection networkManager : disabledFlushes) {
|
|
||||||
+ networkManager.enableAutomaticFlush();
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ // Paper end - controlled flush for entity tracker packets
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,215 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
||||||
Date: Thu, 7 May 2020 05:48:54 -0700
|
|
||||||
Subject: [PATCH] Optimise chunk tick iteration
|
|
||||||
|
|
||||||
Use a dedicated list of entity ticking chunks to reduce the cost
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
|
||||||
this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
|
|
||||||
this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
|
|
||||||
// Paper end - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
+ // Paper start - optimise chunk tick iteration
|
|
||||||
+ if (this.needsBroadcastChanges()) {
|
|
||||||
+ this.chunkMap.needsChangeBroadcasting.add(this);
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimise chunk tick iteration
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onChunkRemove() {
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
|
||||||
this.playersInMobSpawnRange = null;
|
|
||||||
this.playersInChunkTickRange = null;
|
|
||||||
// Paper end - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
+ // Paper start - optimise chunk tick iteration
|
|
||||||
+ if (this.needsBroadcastChanges()) {
|
|
||||||
+ this.chunkMap.needsChangeBroadcasting.remove(this);
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimise chunk tick iteration
|
|
||||||
}
|
|
||||||
// Paper end
|
|
||||||
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
|
||||||
|
|
||||||
if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296
|
|
||||||
if (this.changedBlocksPerSection[i] == null) {
|
|
||||||
- this.hasChangedSections = true;
|
|
||||||
+ this.hasChangedSections = true; this.addToBroadcastMap(); // Paper - optimise chunk tick iteration
|
|
||||||
this.changedBlocksPerSection[i] = new ShortOpenHashSet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
|
||||||
int k = this.lightEngine.getMaxLightSection();
|
|
||||||
|
|
||||||
if (y >= j && y <= k) {
|
|
||||||
+ this.addToBroadcastMap(); // Paper - optimise chunk tick iteration
|
|
||||||
int l = y - j;
|
|
||||||
|
|
||||||
if (lightType == LightLayer.SKY) {
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+ // Paper start - optimise chunk tick iteration
|
|
||||||
+ public final boolean needsBroadcastChanges() {
|
|
||||||
+ return this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty();
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ private void addToBroadcastMap() {
|
|
||||||
+ org.spigotmc.AsyncCatcher.catchOp("ChunkHolder update");
|
|
||||||
+ this.chunkMap.needsChangeBroadcasting.add(this);
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimise chunk tick iteration
|
|
||||||
+
|
|
||||||
public void broadcastChanges(LevelChunk chunk) {
|
|
||||||
- if (this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty()) {
|
|
||||||
+ if (this.needsBroadcastChanges()) { // Paper - moved into above, other logic needs to call
|
|
||||||
Level world = chunk.getLevel();
|
|
||||||
List list;
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
@@ -0,0 +0,0 @@ import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
// CraftBukkit end
|
|
||||||
|
|
||||||
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper
|
|
||||||
+
|
|
||||||
public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider {
|
|
||||||
|
|
||||||
private static final byte CHUNK_TYPE_REPLACEABLE = -1;
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
private final Queue<Runnable> unloadQueue;
|
|
||||||
int viewDistance;
|
|
||||||
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper
|
|
||||||
+ public final ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new ReferenceOpenHashSet<>();
|
|
||||||
|
|
||||||
// Paper - rewrite chunk system
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
||||||
@@ -0,0 +0,0 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
|
|
||||||
import net.minecraft.world.level.storage.DimensionDataStorage;
|
|
||||||
import net.minecraft.world.level.storage.LevelData;
|
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
|
||||||
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper
|
|
||||||
|
|
||||||
public class ServerChunkCache extends ChunkSource {
|
|
||||||
|
|
||||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
|
||||||
|
|
||||||
this.lastSpawnState = spawnercreature_d;
|
|
||||||
gameprofilerfiller.popPush("filteringLoadedChunks");
|
|
||||||
- List<ServerChunkCache.ChunkAndHolder> list = Lists.newArrayListWithCapacity(l);
|
|
||||||
- Iterator iterator = this.chunkMap.getChunks().iterator();
|
|
||||||
+ // Paper - moved down
|
|
||||||
this.level.timings.chunkTicks.startTiming(); // Paper
|
|
||||||
|
|
||||||
- while (iterator.hasNext()) {
|
|
||||||
- ChunkHolder playerchunk = (ChunkHolder) iterator.next();
|
|
||||||
- LevelChunk chunk = playerchunk.getTickingChunk();
|
|
||||||
-
|
|
||||||
- if (chunk != null) {
|
|
||||||
- list.add(new ServerChunkCache.ChunkAndHolder(chunk, playerchunk));
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
+ // Paper - moved down
|
|
||||||
|
|
||||||
gameprofilerfiller.popPush("spawnAndTick");
|
|
||||||
boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
|
|
||||||
|
|
||||||
- Collections.shuffle(list);
|
|
||||||
+ // Paper - only shuffle if per-player mob spawning is disabled
|
|
||||||
// Paper - moved natural spawn event up
|
|
||||||
- Iterator iterator1 = list.iterator();
|
|
||||||
|
|
||||||
+ // Paper start - optimise chunk tick iteratio
|
|
||||||
+ Iterator<LevelChunk> iterator1;
|
|
||||||
+ if (this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
|
||||||
+ iterator1 = this.entityTickingChunks.iterator();
|
|
||||||
+ } else {
|
|
||||||
+ iterator1 = this.entityTickingChunks.unsafeIterator();
|
|
||||||
+ List<LevelChunk> shuffled = Lists.newArrayListWithCapacity(this.entityTickingChunks.size());
|
|
||||||
+ while (iterator1.hasNext()) {
|
|
||||||
+ shuffled.add(iterator1.next());
|
|
||||||
+ }
|
|
||||||
+ Collections.shuffle(shuffled);
|
|
||||||
+ iterator1 = shuffled.iterator();
|
|
||||||
+ }
|
|
||||||
+ try {
|
|
||||||
while (iterator1.hasNext()) {
|
|
||||||
- ServerChunkCache.ChunkAndHolder chunkproviderserver_a = (ServerChunkCache.ChunkAndHolder) iterator1.next();
|
|
||||||
- LevelChunk chunk1 = chunkproviderserver_a.chunk;
|
|
||||||
+ LevelChunk chunk1 = iterator1.next();
|
|
||||||
+ ChunkHolder holder = chunk1.playerChunk;
|
|
||||||
+ if (holder != null) {
|
|
||||||
+ // Paper - move down
|
|
||||||
+ // Paper end - optimise chunk tick iteration
|
|
||||||
ChunkPos chunkcoordintpair = chunk1.getPos();
|
|
||||||
|
|
||||||
- if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
+ if ((true || this.level.isNaturalSpawningAllowed(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning // Paper - the chunk is known ticking
|
|
||||||
chunk1.incrementInhabitedTime(j);
|
|
||||||
- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
+ if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration
|
|
||||||
NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1);
|
|
||||||
}
|
|
||||||
|
|
||||||
- if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) {
|
|
||||||
+ if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - the chunk is known ticking
|
|
||||||
this.level.tickChunk(chunk1, k);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+ // Paper start - optimise chunk tick iteration
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ } finally {
|
|
||||||
+ if (iterator1 instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator safeIterator) {
|
|
||||||
+ safeIterator.finishedIterating();
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
+ // Paper end - optimise chunk tick iteration
|
|
||||||
this.level.timings.chunkTicks.stopTiming(); // Paper
|
|
||||||
gameprofilerfiller.popPush("customSpawners");
|
|
||||||
if (flag2) {
|
|
||||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
|
||||||
this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies);
|
|
||||||
} // Paper - timings
|
|
||||||
}
|
|
||||||
-
|
|
||||||
- gameprofilerfiller.popPush("broadcast");
|
|
||||||
- list.forEach((chunkproviderserver_a1) -> {
|
|
||||||
- this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
|
|
||||||
- chunkproviderserver_a1.holder.broadcastChanges(chunkproviderserver_a1.chunk);
|
|
||||||
- this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
|
|
||||||
- });
|
|
||||||
gameprofilerfiller.pop();
|
|
||||||
+ // Paper start - use set of chunks requiring updates, rather than iterating every single one loaded
|
|
||||||
+ gameprofilerfiller.popPush("broadcast");
|
|
||||||
+ this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
|
|
||||||
+ if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) {
|
|
||||||
+ ReferenceOpenHashSet<ChunkHolder> copy = this.chunkMap.needsChangeBroadcasting.clone();
|
|
||||||
+ this.chunkMap.needsChangeBroadcasting.clear();
|
|
||||||
+ for (ChunkHolder holder : copy) {
|
|
||||||
+ holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded
|
|
||||||
+ if (holder.needsBroadcastChanges()) {
|
|
||||||
+ // I DON'T want to KNOW what DUMB plugins might be doing.
|
|
||||||
+ this.chunkMap.needsChangeBroadcasting.add(holder);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
|
|
||||||
gameprofilerfiller.pop();
|
|
||||||
+ // Paper end - use set of chunks requiring updates, rather than iterating every single one loaded
|
|
||||||
this.chunkMap.tick();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,426 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
||||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
|
||||||
this.chunkMap.needsChangeBroadcasting.add(this);
|
|
||||||
}
|
|
||||||
// Paper end - optimise chunk tick iteration
|
|
||||||
+ // Paper start - optimise checkDespawn
|
|
||||||
+ LevelChunk chunk = this.getFullChunkNowUnchecked();
|
|
||||||
+ if (chunk != null) {
|
|
||||||
+ chunk.updateGeneralAreaCache();
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimise checkDespawn
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onChunkRemove() {
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
|
||||||
this.chunkMap.needsChangeBroadcasting.remove(this);
|
|
||||||
}
|
|
||||||
// Paper end - optimise chunk tick iteration
|
|
||||||
+ // Paper start - optimise checkDespawn
|
|
||||||
+ LevelChunk chunk = this.getFullChunkNowUnchecked();
|
|
||||||
+ if (chunk != null) {
|
|
||||||
+ chunk.removeGeneralAreaCache();
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimise checkDespawn
|
|
||||||
}
|
|
||||||
// Paper end
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
public final ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new ReferenceOpenHashSet<>();
|
|
||||||
|
|
||||||
// Paper - rewrite chunk system
|
|
||||||
+ // 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
|
|
||||||
|
|
||||||
// Paper start - distance maps
|
|
||||||
private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>();
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
trackMap.add(player, chunkX, chunkZ, Math.min(trackRange, io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player)));
|
|
||||||
}
|
|
||||||
// Paper end - use distance map to optimise entity tracker
|
|
||||||
+ this.playerGeneralAreaMap.add(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS); // Paper - optimise checkDespawn
|
|
||||||
}
|
|
||||||
|
|
||||||
void removePlayerFromDistanceMaps(ServerPlayer player) {
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
this.playerMobSpawnMap.remove(player);
|
|
||||||
this.playerChunkTickRangeMap.remove(player);
|
|
||||||
// Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
||||||
+ this.playerGeneralAreaMap.remove(player); // Paper - optimise checkDespawns
|
|
||||||
// Paper start - per player mob spawning
|
|
||||||
if (this.playerMobDistanceMap != null) {
|
|
||||||
this.playerMobDistanceMap.remove(player);
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
trackMap.update(player, chunkX, chunkZ, Math.min(trackRange, io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player)));
|
|
||||||
}
|
|
||||||
// Paper end - use distance map to optimise entity tracker
|
|
||||||
+ this.playerGeneralAreaMap.update(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS); // Paper - optimise checkDespawn
|
|
||||||
}
|
|
||||||
// Paper end
|
|
||||||
// Paper start
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
||||||
+ // Paper start - optimise checkDespawn
|
|
||||||
+ this.playerGeneralAreaMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
|
|
||||||
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
||||||
+ LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ);
|
|
||||||
+ if (chunk != null) {
|
|
||||||
+ chunk.updateGeneralAreaCache(newState);
|
|
||||||
+ }
|
|
||||||
+ },
|
|
||||||
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
||||||
+ LevelChunk chunk = ChunkMap.this.level.getChunkSource().getChunkAtIfCachedImmediately(rangeX, rangeZ);
|
|
||||||
+ if (chunk != null) {
|
|
||||||
+ chunk.updateGeneralAreaCache(newState);
|
|
||||||
+ }
|
|
||||||
+ });
|
|
||||||
+ // Paper end - optimise checkDespawn
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ChunkGenerator generator() {
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
||||||
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
||||||
}
|
|
||||||
// Paper end
|
|
||||||
|
|
||||||
+ // Paper start - optimise checkDespawn
|
|
||||||
+ public final List<ServerPlayer> playersAffectingSpawning = new java.util.ArrayList<>();
|
|
||||||
+ // Paper end - optimise checkDespawn
|
|
||||||
+ // Paper start - optimise get nearest players for entity AI
|
|
||||||
+ @Override
|
|
||||||
+ public final ServerPlayer getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions condition, @Nullable LivingEntity source,
|
|
||||||
+ double centerX, double centerY, double centerZ) {
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> nearby;
|
|
||||||
+ nearby = this.getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(Mth.floor(centerX) >> 4, Mth.floor(centerZ) >> 4);
|
|
||||||
+
|
|
||||||
+ if (nearby == null) {
|
|
||||||
+ return null;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ Object[] backingSet = nearby.getBackingSet();
|
|
||||||
+
|
|
||||||
+ double closestDistanceSquared = Double.MAX_VALUE;
|
|
||||||
+ ServerPlayer closest = null;
|
|
||||||
+
|
|
||||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
|
||||||
+ Object _player = backingSet[i];
|
|
||||||
+ if (!(_player instanceof ServerPlayer)) {
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ ServerPlayer player = (ServerPlayer)_player;
|
|
||||||
+
|
|
||||||
+ double distanceSquared = player.distanceToSqr(centerX, centerY, centerZ);
|
|
||||||
+ if (distanceSquared < closestDistanceSquared && condition.test(source, player)) {
|
|
||||||
+ closest = player;
|
|
||||||
+ closestDistanceSquared = distanceSquared;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return closest;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ @Override
|
|
||||||
+ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions pathfindertargetcondition, LivingEntity entityliving) {
|
|
||||||
+ return this.getNearestPlayer(pathfindertargetcondition, entityliving, entityliving.getX(), entityliving.getY(), entityliving.getZ());
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ @Override
|
|
||||||
+ public Player getNearestPlayer(net.minecraft.world.entity.ai.targeting.TargetingConditions pathfindertargetcondition,
|
|
||||||
+ double d0, double d1, double d2) {
|
|
||||||
+ return this.getNearestPlayer(pathfindertargetcondition, null, d0, d1, d2);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ @Override
|
|
||||||
+ public List<Player> getNearbyPlayers(net.minecraft.world.entity.ai.targeting.TargetingConditions condition, LivingEntity source, AABB axisalignedbb) {
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> nearby;
|
|
||||||
+ double centerX = (axisalignedbb.maxX + axisalignedbb.minX) * 0.5;
|
|
||||||
+ double centerZ = (axisalignedbb.maxZ + axisalignedbb.minZ) * 0.5;
|
|
||||||
+ nearby = this.getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(Mth.floor(centerX) >> 4, Mth.floor(centerZ) >> 4);
|
|
||||||
+
|
|
||||||
+ List<Player> ret = new java.util.ArrayList<>();
|
|
||||||
+
|
|
||||||
+ if (nearby == null) {
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ Object[] backingSet = nearby.getBackingSet();
|
|
||||||
+
|
|
||||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
|
||||||
+ Object _player = backingSet[i];
|
|
||||||
+ if (!(_player instanceof ServerPlayer)) {
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ ServerPlayer player = (ServerPlayer)_player;
|
|
||||||
+
|
|
||||||
+ if (axisalignedbb.contains(player.getX(), player.getY(), player.getZ()) && condition.test(source, player)) {
|
|
||||||
+ ret.add(player);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimise get nearest players for entity AI
|
|
||||||
+
|
|
||||||
// Add env and gen to constructor, IWorldDataServer -> WorldDataServer
|
|
||||||
public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, @Nullable RandomSequences randomsequences, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
|
|
||||||
// IRegistryCustom.Dimension iregistrycustom_dimension = minecraftserver.registryAccess(); // CraftBukkit - decompile error
|
|
||||||
@@ -0,0 +0,0 @@ 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.PLAYER_AFFECTS_SPAWNING.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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/world/entity/Mob.java
|
|
||||||
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
|
|
||||||
@@ -0,0 +0,0 @@ public abstract class Mob extends LivingEntity implements Targeting {
|
|
||||||
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.PLAYER_AFFECTS_SPAWNING); // Paper
|
|
||||||
+ // Paper start - optimise checkDespawn
|
|
||||||
+ Player entityhuman = this.level().findNearbyPlayer(this, level().paperConfig().entities.spawning.despawnRanges.get(this.getType().getCategory()).hard() + 1, EntitySelector.PLAYER_AFFECTS_SPAWNING); // 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
||||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
||||||
@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
||||||
return this.getChunkIfLoaded(chunkX, chunkZ) != null;
|
|
||||||
}
|
|
||||||
// Paper end
|
|
||||||
+ // Paper start - optimise checkDespawn
|
|
||||||
+ public final List<net.minecraft.server.level.ServerPlayer> getNearbyPlayers(@Nullable Entity source, double sourceX, double sourceY,
|
|
||||||
+ double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) {
|
|
||||||
+ LevelChunk chunk;
|
|
||||||
+ if (maxRange < 0.0 || maxRange >= net.minecraft.server.level.ChunkMap.GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE ||
|
|
||||||
+ (chunk = (LevelChunk)this.getChunkIfLoadedImmediately(Mth.floor(sourceX) >> 4, Mth.floor(sourceZ) >> 4)) == null) {
|
|
||||||
+ return this.getNearbyPlayersSlow(source, sourceX, sourceY, sourceZ, maxRange, predicate);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ List<net.minecraft.server.level.ServerPlayer> ret = new java.util.ArrayList<>();
|
|
||||||
+ chunk.getNearestPlayers(sourceX, sourceY, sourceZ, predicate, maxRange, ret);
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ private List<net.minecraft.server.level.ServerPlayer> getNearbyPlayersSlow(@Nullable Entity source, double sourceX, double sourceY,
|
|
||||||
+ double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) {
|
|
||||||
+ List<net.minecraft.server.level.ServerPlayer> ret = new java.util.ArrayList<>();
|
|
||||||
+ double maxRangeSquared = maxRange * maxRange;
|
|
||||||
+
|
|
||||||
+ for (net.minecraft.server.level.ServerPlayer player : (List<net.minecraft.server.level.ServerPlayer>)this.players()) {
|
|
||||||
+ if ((maxRange < 0.0 || player.distanceToSqr(sourceX, sourceY, sourceZ) < maxRangeSquared)) {
|
|
||||||
+ if (predicate == null || predicate.test(player)) {
|
|
||||||
+ ret.add(player);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ private net.minecraft.server.level.ServerPlayer getNearestPlayerSlow(@Nullable Entity source, double sourceX, double sourceY,
|
|
||||||
+ double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) {
|
|
||||||
+ net.minecraft.server.level.ServerPlayer closest = null;
|
|
||||||
+ double closestRangeSquared = maxRange < 0.0 ? Double.MAX_VALUE : maxRange * maxRange;
|
|
||||||
+
|
|
||||||
+ for (net.minecraft.server.level.ServerPlayer player : (List<net.minecraft.server.level.ServerPlayer>)this.players()) {
|
|
||||||
+ double distanceSquared = player.distanceToSqr(sourceX, sourceY, sourceZ);
|
|
||||||
+ if (distanceSquared < closestRangeSquared && (predicate == null || predicate.test(player))) {
|
|
||||||
+ closest = player;
|
|
||||||
+ closestRangeSquared = distanceSquared;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return closest;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ public final net.minecraft.server.level.ServerPlayer getNearestPlayer(@Nullable Entity source, double sourceX, double sourceY,
|
|
||||||
+ double sourceZ, double maxRange, @Nullable Predicate<Entity> predicate) {
|
|
||||||
+ LevelChunk chunk;
|
|
||||||
+ if (maxRange < 0.0 || maxRange >= net.minecraft.server.level.ChunkMap.GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE ||
|
|
||||||
+ (chunk = (LevelChunk)this.getChunkIfLoadedImmediately(Mth.floor(sourceX) >> 4, Mth.floor(sourceZ) >> 4)) == null) {
|
|
||||||
+ return this.getNearestPlayerSlow(source, sourceX, sourceY, sourceZ, maxRange, predicate);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return chunk.findNearestPlayer(sourceX, sourceY, sourceZ, maxRange, predicate);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ @Override
|
|
||||||
+ public @Nullable Player getNearestPlayer(double d0, double d1, double d2, double d3, @Nullable Predicate<Entity> predicate) {
|
|
||||||
+ return this.getNearestPlayer(null, d0, d1, d2, d3, predicate);
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimise checkDespawn
|
|
||||||
|
|
||||||
public abstract ResourceKey<LevelStem> getTypeKey();
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
||||||
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
||||||
@@ -0,0 +0,0 @@ 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);
|
|
||||||
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) {
|
|
||||||
- return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerToCenterThan(new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isNaturalSpawningAllowed((BlockPos) pos));
|
|
||||||
+ return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerToCenterThan(new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isNaturalSpawningAllowed((BlockPos) pos)); // Paper - diff on change, copy into caller
|
|
||||||
}
|
|
||||||
|
|
||||||
// Paper start
|
|
||||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
||||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
||||||
@@ -0,0 +0,0 @@ public class LevelChunk extends ChunkAccess {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Paper end
|
|
||||||
+ // Paper start - optimise checkDespawn
|
|
||||||
+ private boolean playerGeneralAreaCacheSet;
|
|
||||||
+ private com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> playerGeneralAreaCache;
|
|
||||||
+
|
|
||||||
+ public com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> getPlayerGeneralAreaCache() {
|
|
||||||
+ if (!this.playerGeneralAreaCacheSet) {
|
|
||||||
+ this.updateGeneralAreaCache();
|
|
||||||
+ }
|
|
||||||
+ return this.playerGeneralAreaCache;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public void updateGeneralAreaCache() {
|
|
||||||
+ this.updateGeneralAreaCache(((ServerLevel)this.level).getChunkSource().chunkMap.playerGeneralAreaMap.getObjectsInRange(this.coordinateKey));
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public void removeGeneralAreaCache() {
|
|
||||||
+ this.playerGeneralAreaCacheSet = false;
|
|
||||||
+ this.playerGeneralAreaCache = null;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public void updateGeneralAreaCache(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> value) {
|
|
||||||
+ this.playerGeneralAreaCacheSet = true;
|
|
||||||
+ this.playerGeneralAreaCache = value;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public net.minecraft.server.level.ServerPlayer findNearestPlayer(double sourceX, double sourceY, double sourceZ,
|
|
||||||
+ double maxRange, java.util.function.Predicate<Entity> predicate) {
|
|
||||||
+ if (!this.playerGeneralAreaCacheSet) {
|
|
||||||
+ this.updateGeneralAreaCache();
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> nearby = this.playerGeneralAreaCache;
|
|
||||||
+
|
|
||||||
+ if (nearby == null) {
|
|
||||||
+ return null;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ Object[] backingSet = nearby.getBackingSet();
|
|
||||||
+ double closestDistance = maxRange < 0.0 ? Double.MAX_VALUE : maxRange * maxRange;
|
|
||||||
+ net.minecraft.server.level.ServerPlayer closest = null;
|
|
||||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
|
||||||
+ Object _player = backingSet[i];
|
|
||||||
+ if (!(_player instanceof net.minecraft.server.level.ServerPlayer)) {
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer)_player;
|
|
||||||
+
|
|
||||||
+ double distance = player.distanceToSqr(sourceX, sourceY, sourceZ);
|
|
||||||
+ if (distance < closestDistance && predicate.test(player)) {
|
|
||||||
+ closest = player;
|
|
||||||
+ closestDistance = distance;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return closest;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public void getNearestPlayers(double sourceX, double sourceY, double sourceZ, java.util.function.Predicate<Entity> predicate,
|
|
||||||
+ double range, java.util.List<net.minecraft.server.level.ServerPlayer> ret) {
|
|
||||||
+ if (!this.playerGeneralAreaCacheSet) {
|
|
||||||
+ this.updateGeneralAreaCache();
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<net.minecraft.server.level.ServerPlayer> nearby = this.playerGeneralAreaCache;
|
|
||||||
+
|
|
||||||
+ if (nearby == null) {
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ double rangeSquared = range * range;
|
|
||||||
+
|
|
||||||
+ Object[] backingSet = nearby.getBackingSet();
|
|
||||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
|
||||||
+ Object _player = backingSet[i];
|
|
||||||
+ if (!(_player instanceof net.minecraft.server.level.ServerPlayer)) {
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ net.minecraft.server.level.ServerPlayer player = (net.minecraft.server.level.ServerPlayer)_player;
|
|
||||||
+
|
|
||||||
+ if (range >= 0.0) {
|
|
||||||
+ double distanceSquared = player.distanceToSqr(sourceX, sourceY, sourceZ);
|
|
||||||
+ if (distanceSquared > rangeSquared) {
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (predicate == null || predicate.test(player)) {
|
|
||||||
+ ret.add(player);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimise checkDespawn
|
|
||||||
|
|
||||||
public LevelChunk(ServerLevel world, ProtoChunk protoChunk, @Nullable LevelChunk.PostLoadProcessor entityLoader) {
|
|
||||||
this(world, protoChunk.getPos(), protoChunk.getUpgradeData(), protoChunk.unpackBlockTicks(), protoChunk.unpackFluidTicks(), protoChunk.getInhabitedTime(), protoChunk.getSections(), entityLoader, protoChunk.getBlendingData());
|
|
|
@ -1,46 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
||||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
|
||||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
|
||||||
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
||||||
if (this.channel.eventLoop().inEventLoop()) {
|
|
||||||
this.doSendPacket(packet, callbacks, enumprotocol, enumprotocol1, flush); // Paper
|
|
||||||
} 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) {
|
|
||||||
+ io.netty.util.concurrent.AbstractEventExecutor.LazyRunnable run = () -> {
|
|
||||||
+ this.doSendPacket(packet, callbacks, 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, callbacks, enumprotocol, enumprotocol1, flush); // Paper
|
|
||||||
+ this.doSendPacket(packet, callbacks, enumprotocol, enumprotocol1, flush); // Paper - add flush parameter // Paper - diff on change
|
|
||||||
});
|
|
||||||
+ } // Paper
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,346 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
||||||
Date: Tue, 5 May 2020 20:40:53 -0700
|
|
||||||
Subject: [PATCH] Optimize anyPlayerCloseEnoughForSpawning to use distance maps
|
|
||||||
|
|
||||||
Use a distance map to find the players in range quickly
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkHolder {
|
|
||||||
|
|
||||||
// Paper start
|
|
||||||
public void onChunkAdd() {
|
|
||||||
-
|
|
||||||
+ // Paper start - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
+ long key = io.papermc.paper.util.MCUtil.getCoordinateKey(this.pos);
|
|
||||||
+ this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
|
|
||||||
+ this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
|
|
||||||
+ // Paper end - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onChunkRemove() {
|
|
||||||
-
|
|
||||||
+ // Paper start - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
+ this.playersInMobSpawnRange = null;
|
|
||||||
+ this.playersInChunkTickRange = null;
|
|
||||||
+ // Paper end - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
}
|
|
||||||
// Paper end
|
|
||||||
|
|
||||||
public final io.papermc.paper.chunk.system.scheduling.NewChunkHolder newChunkHolder; // Paper - rewrite chunk system
|
|
||||||
|
|
||||||
+ // Paper start - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
+ // cached here to avoid a map lookup
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInMobSpawnRange;
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInChunkTickRange;
|
|
||||||
+ // Paper end - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
+
|
|
||||||
// Paper start - replace player chunk loader
|
|
||||||
private final com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> playersSentChunkTo = new com.destroystokyo.paper.util.maplist.ReferenceList<>();
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
return net.minecraft.server.MinecraftServer.getServer().getScaledTrackingDistance(vanilla);
|
|
||||||
}
|
|
||||||
// Paper end - use distance map to optimise tracker
|
|
||||||
+ // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
||||||
+ // A note about the naming used here:
|
|
||||||
+ // Previously, mojang used a "spawn range" of 8 for controlling both ticking and
|
|
||||||
+ // mob spawn range. However, spigot makes the spawn range configurable by
|
|
||||||
+ // checking if the chunk is in the tick range (8) and the spawn range
|
|
||||||
+ // obviously this means a spawn range > 8 cannot be implemented
|
|
||||||
+
|
|
||||||
+ // these maps are named after spigot's uses
|
|
||||||
+ 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 ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
||||||
|
|
||||||
void addPlayerToDistanceMaps(ServerPlayer player) {
|
|
||||||
this.level.playerChunkLoader.addPlayer(player); // Paper - replace chunk loader
|
|
||||||
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
|
|
||||||
+ this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
||||||
// Paper start - per player mob spawning
|
|
||||||
if (this.playerMobDistanceMap != null) {
|
|
||||||
this.playerMobDistanceMap.add(player, chunkX, chunkZ, io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player));
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
void removePlayerFromDistanceMaps(ServerPlayer player) {
|
|
||||||
this.level.playerChunkLoader.removePlayer(player); // Paper - replace chunk loader
|
|
||||||
|
|
||||||
+ // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
||||||
+ this.playerMobSpawnMap.remove(player);
|
|
||||||
+ this.playerChunkTickRangeMap.remove(player);
|
|
||||||
+ // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
||||||
// Paper start - per player mob spawning
|
|
||||||
if (this.playerMobDistanceMap != null) {
|
|
||||||
this.playerMobDistanceMap.remove(player);
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
|
|
||||||
// Note: players need to be explicitly added to distance maps before they can be updated
|
|
||||||
this.level.playerChunkLoader.updatePlayer(player); // Paper - replace chunk loader
|
|
||||||
+ this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
||||||
// Paper start - per player mob spawning
|
|
||||||
if (this.playerMobDistanceMap != null) {
|
|
||||||
this.playerMobDistanceMap.update(player, chunkX, chunkZ, io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player));
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
this.playerEntityTrackerTrackMaps[ordinal] = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
|
|
||||||
}
|
|
||||||
// Paper end - use distance map to optimise entity tracker
|
|
||||||
+ // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
||||||
+ this.playerChunkTickRangeMap = 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) -> {
|
|
||||||
+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
|
||||||
+ if (playerChunk != null) {
|
|
||||||
+ playerChunk.playersInChunkTickRange = newState;
|
|
||||||
+ }
|
|
||||||
+ },
|
|
||||||
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
||||||
+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
|
||||||
+ if (playerChunk != null) {
|
|
||||||
+ playerChunk.playersInChunkTickRange = newState;
|
|
||||||
+ }
|
|
||||||
+ });
|
|
||||||
+ this.playerMobSpawnMap = 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) -> {
|
|
||||||
+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
|
||||||
+ if (playerChunk != null) {
|
|
||||||
+ playerChunk.playersInMobSpawnRange = newState;
|
|
||||||
+ }
|
|
||||||
+ },
|
|
||||||
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
|
||||||
+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
|
||||||
+ if (playerChunk != null) {
|
|
||||||
+ playerChunk.playersInMobSpawnRange = newState;
|
|
||||||
+ }
|
|
||||||
+ });
|
|
||||||
+ // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ChunkGenerator generator() {
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
return this.anyPlayerCloseEnoughForSpawning(pos, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
- boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkcoordintpair, boolean reducedRange) {
|
|
||||||
- int chunkRange = level.spigotConfig.mobSpawnRange;
|
|
||||||
- chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
|
|
||||||
- chunkRange = (chunkRange > 8) ? 8 : chunkRange;
|
|
||||||
-
|
|
||||||
- final int finalChunkRange = chunkRange; // Paper for lambda below
|
|
||||||
- //double blockRange = (reducedRange) ? Math.pow(chunkRange << 4, 2) : 16384.0D; // Paper - use from event
|
|
||||||
- double blockRange = 16384.0D; // Paper
|
|
||||||
- // Spigot end
|
|
||||||
- long i = chunkcoordintpair.toLong();
|
|
||||||
+ // Paper start - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
+ final boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkcoordintpair, boolean reducedRange) {
|
|
||||||
+ return this.anyPlayerCloseEnoughForSpawning(this.getUpdatingChunkIfPresent(chunkcoordintpair.toLong()), chunkcoordintpair, reducedRange);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- if (!this.distanceManager.hasPlayersNearby(i)) {
|
|
||||||
+ final boolean anyPlayerCloseEnoughForSpawning(ChunkHolder playerchunk, ChunkPos chunkcoordintpair, boolean reducedRange) {
|
|
||||||
+ // this function is so hot that removing the map lookup call can have an order of magnitude impact on its performance
|
|
||||||
+ // tested and confirmed via System.nanoTime()
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInRange = reducedRange ? playerchunk.playersInMobSpawnRange : playerchunk.playersInChunkTickRange;
|
|
||||||
+ if (playersInRange == null) {
|
|
||||||
return false;
|
|
||||||
- } else {
|
|
||||||
- Iterator iterator = this.playerMap.getPlayers(i).iterator();
|
|
||||||
-
|
|
||||||
- ServerPlayer entityplayer;
|
|
||||||
+ }
|
|
||||||
+ Object[] backingSet = playersInRange.getBackingSet();
|
|
||||||
|
|
||||||
- do {
|
|
||||||
- if (!iterator.hasNext()) {
|
|
||||||
- return false;
|
|
||||||
+ if (reducedRange) {
|
|
||||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
|
||||||
+ Object raw = backingSet[i];
|
|
||||||
+ if (!(raw instanceof ServerPlayer player)) {
|
|
||||||
+ continue;
|
|
||||||
}
|
|
||||||
-
|
|
||||||
- entityplayer = (ServerPlayer) iterator.next();
|
|
||||||
- // Paper start - add PlayerNaturallySpawnCreaturesEvent
|
|
||||||
- com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event;
|
|
||||||
- blockRange = 16384.0D;
|
|
||||||
- if (reducedRange) {
|
|
||||||
- event = entityplayer.playerNaturallySpawnedEvent;
|
|
||||||
- if (event == null || event.isCancelled()) return false;
|
|
||||||
- blockRange = (double) ((event.getSpawnRadius() << 4) * (event.getSpawnRadius() << 4));
|
|
||||||
+ // don't check spectator and whatnot, already handled by mob spawn map update
|
|
||||||
+ if (euclideanDistanceSquared(chunkcoordintpair, player) < player.lastEntitySpawnRadiusSquared) {
|
|
||||||
+ return true; // in range
|
|
||||||
}
|
|
||||||
- // Paper end
|
|
||||||
- } while (!this.playerIsCloseEnoughForSpawning(entityplayer, chunkcoordintpair, blockRange)); // Spigot
|
|
||||||
-
|
|
||||||
- return true;
|
|
||||||
+ }
|
|
||||||
+ } else {
|
|
||||||
+ final double range = (DistanceManager.MOB_SPAWN_RANGE * 16) * (DistanceManager.MOB_SPAWN_RANGE * 16);
|
|
||||||
+ // before spigot, mob spawn range was actually mob spawn range + tick range, but it was split
|
|
||||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
|
||||||
+ Object raw = backingSet[i];
|
|
||||||
+ if (!(raw instanceof ServerPlayer player)) {
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ // don't check spectator and whatnot, already handled by mob spawn map update
|
|
||||||
+ if (euclideanDistanceSquared(chunkcoordintpair, player) < range) {
|
|
||||||
+ return true; // in range
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
+ // no players in range
|
|
||||||
+ return false;
|
|
||||||
+ // Paper end - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ServerPlayer> getPlayersCloseForSpawning(ChunkPos pos) {
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
|
|
||||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
|
||||||
private static final int INITIAL_TICKET_LIST_CAPACITY = 4;
|
|
||||||
final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap();
|
|
||||||
// Paper - rewrite chunk system
|
|
||||||
- private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8);
|
|
||||||
+ public static final int MOB_SPAWN_RANGE = 8; // private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8); // Paper - no longer used
|
|
||||||
// Paper - rewrite chunk system
|
|
||||||
private final ChunkMap chunkMap; // Paper
|
|
||||||
|
|
||||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
|
||||||
long i = chunkcoordintpair.toLong();
|
|
||||||
|
|
||||||
// Paper - no longer used
|
|
||||||
- this.naturalSpawnChunkCounter.update(i, 0, true);
|
|
||||||
+ //this.naturalSpawnChunkCounter.update(i, 0, true); // Paper - no longer used
|
|
||||||
//this.playerTicketManager.update(i, 0, true); // Paper - no longer used
|
|
||||||
//this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
|
|
||||||
}
|
|
||||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
|
||||||
if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully.
|
|
||||||
if (objectset == null || objectset.isEmpty()) { // Paper
|
|
||||||
this.playersPerChunk.remove(i);
|
|
||||||
- this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false);
|
|
||||||
+ // this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false); // Paper - no longer used
|
|
||||||
//this.playerTicketManager.update(i, Integer.MAX_VALUE, false); // Paper - no longer used
|
|
||||||
//this.tickingTicketsTracker.removeTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
|
|
||||||
}
|
|
||||||
@@ -0,0 +0,0 @@ public abstract class DistanceManager {
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNaturalSpawnChunkCount() {
|
|
||||||
- this.naturalSpawnChunkCounter.runAllUpdates();
|
|
||||||
- return this.naturalSpawnChunkCounter.chunks.size();
|
|
||||||
+ // Paper start - use distance map to implement
|
|
||||||
+ // note: this is the spawn chunk count
|
|
||||||
+ return this.chunkMap.playerChunkTickRangeMap.size();
|
|
||||||
+ // Paper end - use distance map to implement
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasPlayersNearby(long chunkPos) {
|
|
||||||
- this.naturalSpawnChunkCounter.runAllUpdates();
|
|
||||||
- return this.naturalSpawnChunkCounter.chunks.containsKey(chunkPos);
|
|
||||||
+ // Paper start - use distance map to implement
|
|
||||||
+ // note: this is the is spawn chunk method
|
|
||||||
+ return this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(chunkPos) != null;
|
|
||||||
+ // Paper end - use distance map to implement
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDebugStatus() {
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
||||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
|
||||||
if (flag) {
|
|
||||||
this.chunkMap.tick();
|
|
||||||
} else {
|
|
||||||
+ // Paper start - optimize isOutisdeRange
|
|
||||||
+ ChunkMap playerChunkMap = this.chunkMap;
|
|
||||||
+ for (ServerPlayer player : this.level.players) {
|
|
||||||
+ if (!player.affectsSpawning || player.isSpectator()) {
|
|
||||||
+ playerChunkMap.playerMobSpawnMap.remove(player);
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ int viewDistance = this.chunkMap.getEffectiveViewDistance();
|
|
||||||
+
|
|
||||||
+ // copied and modified from isOutisdeRange
|
|
||||||
+ int chunkRange = level.spigotConfig.mobSpawnRange;
|
|
||||||
+ chunkRange = (chunkRange > viewDistance) ? (byte)viewDistance : chunkRange;
|
|
||||||
+ chunkRange = (chunkRange > DistanceManager.MOB_SPAWN_RANGE) ? DistanceManager.MOB_SPAWN_RANGE : chunkRange;
|
|
||||||
+
|
|
||||||
+ com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(player.getBukkitEntity(), (byte)chunkRange);
|
|
||||||
+ event.callEvent();
|
|
||||||
+ if (event.isCancelled() || event.getSpawnRadius() < 0 || playerChunkMap.playerChunkTickRangeMap.getLastViewDistance(player) == -1) {
|
|
||||||
+ playerChunkMap.playerMobSpawnMap.remove(player);
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ int range = Math.min(event.getSpawnRadius(), 32); // limit to max view distance
|
|
||||||
+ int chunkX = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getX());
|
|
||||||
+ int chunkZ = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getZ());
|
|
||||||
+
|
|
||||||
+ playerChunkMap.playerMobSpawnMap.addOrUpdate(player, chunkX, chunkZ, range);
|
|
||||||
+ player.lastEntitySpawnRadiusSquared = (double)((range << 4) * (range << 4)); // used in anyPlayerCloseEnoughForSpawning
|
|
||||||
+ player.playerNaturallySpawnedEvent = event;
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimize isOutisdeRange
|
|
||||||
LevelData worlddata = this.level.getLevelData();
|
|
||||||
ProfilerFiller gameprofilerfiller = this.level.getProfiler();
|
|
||||||
|
|
||||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
|
||||||
boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
|
|
||||||
|
|
||||||
Collections.shuffle(list);
|
|
||||||
- // Paper start - call player naturally spawn event
|
|
||||||
- int chunkRange = level.spigotConfig.mobSpawnRange;
|
|
||||||
- chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
|
|
||||||
- chunkRange = Math.min(chunkRange, 8);
|
|
||||||
- for (ServerPlayer entityPlayer : this.level.players()) {
|
|
||||||
- entityPlayer.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(entityPlayer.getBukkitEntity(), (byte) chunkRange);
|
|
||||||
- entityPlayer.playerNaturallySpawnedEvent.callEvent();
|
|
||||||
- };
|
|
||||||
- // Paper end
|
|
||||||
+ // Paper - moved natural spawn event up
|
|
||||||
Iterator iterator1 = list.iterator();
|
|
||||||
|
|
||||||
while (iterator1.hasNext()) {
|
|
||||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
|
||||||
LevelChunk chunk1 = chunkproviderserver_a.chunk;
|
|
||||||
ChunkPos chunkcoordintpair = chunk1.getPos();
|
|
||||||
|
|
||||||
- if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair)) {
|
|
||||||
+ if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
chunk1.incrementInhabitedTime(j);
|
|
||||||
- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair, true)) { // Spigot
|
|
||||||
+ if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning
|
|
||||||
NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1);
|
|
||||||
}
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
||||||
@@ -0,0 +0,0 @@ public class ServerPlayer extends Player {
|
|
||||||
public Integer clientViewDistance;
|
|
||||||
// CraftBukkit end
|
|
||||||
public boolean isRealPlayer; // Paper
|
|
||||||
+ public double lastEntitySpawnRadiusSquared; // Paper - optimise isOutsideRange, this field is in blocks
|
|
||||||
public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> cachedSingleHashSet; // Paper
|
|
||||||
public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper
|
|
||||||
public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - there are a lot of changes to do if we change all methods leading to the event
|
|
|
@ -1,58 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
||||||
Date: Mon, 27 Apr 2020 00:04:16 -0700
|
|
||||||
Subject: [PATCH] Reduce allocation of Vec3D by entity tracker
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/network/protocol/game/VecDeltaCodec.java b/src/main/java/net/minecraft/network/protocol/game/VecDeltaCodec.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/network/protocol/game/VecDeltaCodec.java
|
|
||||||
+++ b/src/main/java/net/minecraft/network/protocol/game/VecDeltaCodec.java
|
|
||||||
@@ -0,0 +0,0 @@ import org.jetbrains.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
public class VecDeltaCodec {
|
|
||||||
private static final double TRUNCATION_STEPS = 4096.0D;
|
|
||||||
- private Vec3 base = Vec3.ZERO;
|
|
||||||
+ public Vec3 base = Vec3.ZERO; // Paper
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static long encode(double value) {
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
public void updatePlayer(ServerPlayer player) {
|
|
||||||
org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
|
|
||||||
if (player != this.entity) {
|
|
||||||
+ // Paper start - remove allocation of Vec3D here
|
|
||||||
+ // Vec3 vec3d = player.position().subtract(this.entity.position());
|
|
||||||
+ double vec3d_dx = player.getX() - this.entity.getX();
|
|
||||||
+ double vec3d_dz = player.getZ() - this.entity.getZ();
|
|
||||||
+ // Paper end - remove allocation of Vec3D here
|
|
||||||
Vec3 vec3d = player.position().subtract(this.entity.position());
|
|
||||||
double d0 = (double) Math.min(this.getEffectiveRange(), io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player) * 16); // Paper - per player view distance
|
|
||||||
- double d1 = vec3d.x * vec3d.x + vec3d.z * vec3d.z;
|
|
||||||
+ double d1 = vec3d_dx * vec3d_dx + vec3d_dz * vec3d_dz; // Paper
|
|
||||||
double d2 = d0 * d0;
|
|
||||||
boolean flag = d1 <= d2 && this.entity.broadcastToPlayer(player);
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
||||||
@@ -0,0 +0,0 @@ public class ServerEntity {
|
|
||||||
i = Mth.floor(this.entity.getYRot() * 256.0F / 360.0F);
|
|
||||||
j = Mth.floor(this.entity.getXRot() * 256.0F / 360.0F);
|
|
||||||
Vec3 vec3d = this.entity.trackingPosition();
|
|
||||||
- boolean flag1 = this.positionCodec.delta(vec3d).lengthSqr() >= 7.62939453125E-6D;
|
|
||||||
+ // Paper start - reduce allocation of Vec3D here
|
|
||||||
+ Vec3 base = this.positionCodec.base;
|
|
||||||
+ double vec3d_dx = vec3d.x - base.x;
|
|
||||||
+ double vec3d_dy = vec3d.y - base.y;
|
|
||||||
+ double vec3d_dz = vec3d.z - base.z;
|
|
||||||
+ boolean flag1 = (vec3d_dx * vec3d_dx + vec3d_dy * vec3d_dy + vec3d_dz * vec3d_dz) >= 7.62939453125E-6D;
|
|
||||||
+ // Paper end - reduce allocation of Vec3D here
|
|
||||||
Packet<?> packet1 = null;
|
|
||||||
boolean flag2 = flag1 || this.tickCount % 60 == 0;
|
|
||||||
boolean flag3 = Math.abs(i - this.yRotp) >= 1 || Math.abs(j - this.xRotp) >= 1;
|
|
|
@ -1,187 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
||||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
|
|
||||||
@@ -0,0 +0,0 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
|
||||||
if (this.hasRequiredMemories(entity)) {
|
|
||||||
this.status = Behavior.Status.RUNNING;
|
|
||||||
this.orderPolicy.apply(this.behaviors);
|
|
||||||
- this.runningPolicy.apply(this.behaviors.stream(), world, entity, time);
|
|
||||||
+ this.runningPolicy.apply(this.behaviors.entries, world, entity, time);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
@@ -0,0 +0,0 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void tickOrStop(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
|
|
||||||
+ for (BehaviorControl<? super E> task : this.behaviors) {
|
|
||||||
+ if (task.getStatus() == Behavior.Status.RUNNING) {
|
|
||||||
+ task.tickOrStop(world, entity, time);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ // Paper end
|
|
||||||
if (this.behaviors.stream().noneMatch((task) -> {
|
|
||||||
return task.getStatus() == Behavior.Status.RUNNING;
|
|
||||||
})) {
|
|
||||||
@@ -0,0 +0,0 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
|
||||||
@Override
|
|
||||||
public final void doStop(ServerLevel world, E entity, long time) {
|
|
||||||
this.status = Behavior.Status.STOPPED;
|
|
||||||
- this.behaviors.stream().filter((task) -> {
|
|
||||||
- return task.getStatus() == Behavior.Status.RUNNING;
|
|
||||||
- }).forEach((task) -> {
|
|
||||||
- task.doStop(world, entity, time);
|
|
||||||
- });
|
|
||||||
+ for (BehaviorControl<? super E> behavior : this.behaviors) {
|
|
||||||
+ if (behavior.getStatus() == Behavior.Status.RUNNING) {
|
|
||||||
+ behavior.doStop(world, entity, time);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
this.exitErasedMemories.forEach(entity.getBrain()::eraseMemory);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +0,0 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
|
||||||
public static enum RunningPolicy {
|
|
||||||
RUN_ONE {
|
|
||||||
@Override
|
|
||||||
- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? 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<BehaviorControl<? super E>>> tasks, ServerLevel world, E entity, long time) {
|
|
||||||
+ for (ShufflingList.WeightedEntry<BehaviorControl<? super E>> task : tasks) {
|
|
||||||
+ final BehaviorControl<? super E> behavior = task.getData();
|
|
||||||
+ if (behavior.getStatus() == Behavior.Status.STOPPED && behavior.tryStart(world, entity, time)) {
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ // Paper end - remove streams
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TRY_ALL {
|
|
||||||
@Override
|
|
||||||
- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? 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<BehaviorControl<? super E>>> tasks, ServerLevel world, E entity, long time) {
|
|
||||||
+ for (ShufflingList.WeightedEntry<BehaviorControl<? super E>> task : tasks) {
|
|
||||||
+ final BehaviorControl<? super E> behavior = task.getData();
|
|
||||||
+ if (behavior.getStatus() == Behavior.Status.STOPPED) {
|
|
||||||
+ behavior.tryStart(world, entity, time);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ // Paper end - remove streams
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
- public abstract <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time);
|
|
||||||
+ public abstract <E extends LivingEntity> void apply(List<ShufflingList.WeightedEntry<BehaviorControl<? 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
|
|
||||||
@@ -0,0 +0,0 @@ import java.util.stream.Stream;
|
|
||||||
import net.minecraft.util.RandomSource;
|
|
||||||
|
|
||||||
public class ShufflingList<U> implements Iterable<U> {
|
|
||||||
- protected final List<ShufflingList.WeightedEntry<U>> entries;
|
|
||||||
+ public final List<ShufflingList.WeightedEntry<U>> entries; // Paper - public
|
|
||||||
private final RandomSource random = RandomSource.create();
|
|
||||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
|
|
||||||
@@ -0,0 +0,0 @@ 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(32.0D, 16.0D, 32.0D), (itemEntity) -> {
|
|
||||||
- return true;
|
|
||||||
+ return itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && 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, 32.0D) && entity.hasLineOfSight(entityItem)) {
|
|
||||||
+ for (int i = 0; i < list.size(); i++) {
|
|
||||||
+ ItemEntity entityItem = list.get(i);
|
|
||||||
+ if (entity.hasLineOfSight(entityItem)) {
|
|
||||||
+ // Paper end - remove streams
|
|
||||||
nearest = entityItem;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
|
|
||||||
@@ -0,0 +0,0 @@ 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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,387 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
||||||
Date: Tue, 5 May 2020 20:18:05 -0700
|
|
||||||
Subject: [PATCH] Use distance map to optimise entity tracker
|
|
||||||
|
|
||||||
Use the distance map to find candidate players for tracking.
|
|
||||||
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
|
|
||||||
// Paper start - distance maps
|
|
||||||
private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>();
|
|
||||||
+ // Paper start - use distance map to optimise tracker
|
|
||||||
+ public static boolean isLegacyTrackingEntity(Entity entity) {
|
|
||||||
+ return entity.isLegacyTrackingEntity;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // inlined EnumMap, TrackingRange.TrackingRangeType
|
|
||||||
+ static final org.spigotmc.TrackingRange.TrackingRangeType[] TRACKING_RANGE_TYPES = org.spigotmc.TrackingRange.TrackingRangeType.values();
|
|
||||||
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap[] playerEntityTrackerTrackMaps;
|
|
||||||
+ final int[] entityTrackerTrackRanges;
|
|
||||||
+ public final int getEntityTrackerRange(final int ordinal) {
|
|
||||||
+ return this.entityTrackerTrackRanges[ordinal];
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ private int convertSpigotRangeToVanilla(final int vanilla) {
|
|
||||||
+ return net.minecraft.server.MinecraftServer.getServer().getScaledTrackingDistance(vanilla);
|
|
||||||
+ }
|
|
||||||
+ // Paper end - use distance map to optimise tracker
|
|
||||||
|
|
||||||
void addPlayerToDistanceMaps(ServerPlayer player) {
|
|
||||||
this.level.playerChunkLoader.addPlayer(player); // Paper - replace chunk loader
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
this.playerMobDistanceMap.add(player, chunkX, chunkZ, io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player));
|
|
||||||
}
|
|
||||||
// Paper end - per player mob spawning
|
|
||||||
+ // Paper start - use distance map to optimise entity tracker
|
|
||||||
+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) {
|
|
||||||
+ com.destroystokyo.paper.util.misc.PlayerAreaMap trackMap = this.playerEntityTrackerTrackMaps[i];
|
|
||||||
+ int trackRange = this.entityTrackerTrackRanges[i];
|
|
||||||
+
|
|
||||||
+ trackMap.add(player, chunkX, chunkZ, Math.min(trackRange, io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player)));
|
|
||||||
+ }
|
|
||||||
+ // Paper end - use distance map to optimise entity tracker
|
|
||||||
}
|
|
||||||
|
|
||||||
void removePlayerFromDistanceMaps(ServerPlayer player) {
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
this.playerMobDistanceMap.remove(player);
|
|
||||||
}
|
|
||||||
// Paper end - per player mob spawning
|
|
||||||
+ // Paper start - use distance map to optimise tracker
|
|
||||||
+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) {
|
|
||||||
+ this.playerEntityTrackerTrackMaps[i].remove(player);
|
|
||||||
+ }
|
|
||||||
+ // Paper end - use distance map to optimise tracker
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateMaps(ServerPlayer player) {
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
this.playerMobDistanceMap.update(player, chunkX, chunkZ, io.papermc.paper.chunk.system.ChunkSystem.getTickViewDistance(player));
|
|
||||||
}
|
|
||||||
// Paper end - per player mob spawning
|
|
||||||
+ // Paper start - use distance map to optimise entity tracker
|
|
||||||
+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) {
|
|
||||||
+ com.destroystokyo.paper.util.misc.PlayerAreaMap trackMap = this.playerEntityTrackerTrackMaps[i];
|
|
||||||
+ int trackRange = this.entityTrackerTrackRanges[i];
|
|
||||||
+
|
|
||||||
+ trackMap.update(player, chunkX, chunkZ, Math.min(trackRange, io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player)));
|
|
||||||
+ }
|
|
||||||
+ // Paper end - use distance map to optimise entity tracker
|
|
||||||
}
|
|
||||||
// Paper end
|
|
||||||
// Paper start
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
this.regionManagers.add(this.dataRegionManager);
|
|
||||||
// Paper end
|
|
||||||
this.playerMobDistanceMap = this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets) : null; // Paper
|
|
||||||
+ // Paper start - use distance map to optimise entity tracker
|
|
||||||
+ this.playerEntityTrackerTrackMaps = new com.destroystokyo.paper.util.misc.PlayerAreaMap[TRACKING_RANGE_TYPES.length];
|
|
||||||
+ this.entityTrackerTrackRanges = new int[TRACKING_RANGE_TYPES.length];
|
|
||||||
+
|
|
||||||
+ org.spigotmc.SpigotWorldConfig spigotWorldConfig = this.level.spigotConfig;
|
|
||||||
+
|
|
||||||
+ for (int ordinal = 0, len = TRACKING_RANGE_TYPES.length; ordinal < len; ++ordinal) {
|
|
||||||
+ org.spigotmc.TrackingRange.TrackingRangeType trackingRangeType = TRACKING_RANGE_TYPES[ordinal];
|
|
||||||
+ int configuredSpigotValue;
|
|
||||||
+ switch (trackingRangeType) {
|
|
||||||
+ case PLAYER:
|
|
||||||
+ configuredSpigotValue = spigotWorldConfig.playerTrackingRange;
|
|
||||||
+ break;
|
|
||||||
+ case ANIMAL:
|
|
||||||
+ configuredSpigotValue = spigotWorldConfig.animalTrackingRange;
|
|
||||||
+ break;
|
|
||||||
+ case MONSTER:
|
|
||||||
+ configuredSpigotValue = spigotWorldConfig.monsterTrackingRange;
|
|
||||||
+ break;
|
|
||||||
+ case MISC:
|
|
||||||
+ configuredSpigotValue = spigotWorldConfig.miscTrackingRange;
|
|
||||||
+ break;
|
|
||||||
+ case OTHER:
|
|
||||||
+ configuredSpigotValue = spigotWorldConfig.otherTrackingRange;
|
|
||||||
+ break;
|
|
||||||
+ case ENDERDRAGON:
|
|
||||||
+ configuredSpigotValue = EntityType.ENDER_DRAGON.clientTrackingRange() * 16;
|
|
||||||
+ break;
|
|
||||||
+ case DISPLAY:
|
|
||||||
+ configuredSpigotValue = spigotWorldConfig.displayTrackingRange;
|
|
||||||
+ break;
|
|
||||||
+ default:
|
|
||||||
+ throw new IllegalStateException("Missing case for enum " + trackingRangeType);
|
|
||||||
+ }
|
|
||||||
+ configuredSpigotValue = convertSpigotRangeToVanilla(configuredSpigotValue);
|
|
||||||
+
|
|
||||||
+ int trackRange = (configuredSpigotValue >>> 4) + ((configuredSpigotValue & 15) != 0 ? 1 : 0);
|
|
||||||
+ this.entityTrackerTrackRanges[ordinal] = trackRange;
|
|
||||||
+
|
|
||||||
+ this.playerEntityTrackerTrackMaps[ordinal] = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
|
|
||||||
+ }
|
|
||||||
+ // Paper end - use distance map to optimise entity tracker
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ChunkGenerator generator() {
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
public void move(ServerPlayer player) {
|
|
||||||
- ObjectIterator objectiterator = this.entityMap.values().iterator();
|
|
||||||
-
|
|
||||||
- while (objectiterator.hasNext()) {
|
|
||||||
- ChunkMap.TrackedEntity playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next();
|
|
||||||
-
|
|
||||||
- if (playerchunkmap_entitytracker.entity == player) {
|
|
||||||
- playerchunkmap_entitytracker.updatePlayers(this.level.players());
|
|
||||||
- } else {
|
|
||||||
- playerchunkmap_entitytracker.updatePlayer(player);
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
+ // Paper - delay this logic for the entity tracker tick, no need to duplicate it
|
|
||||||
|
|
||||||
int i = SectionPos.blockToSectionCoord(player.getBlockX());
|
|
||||||
int j = SectionPos.blockToSectionCoord(player.getBlockZ());
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
|
|
||||||
entity.tracker = playerchunkmap_entitytracker; // Paper - Fast access to tracker
|
|
||||||
this.entityMap.put(entity.getId(), playerchunkmap_entitytracker);
|
|
||||||
- playerchunkmap_entitytracker.updatePlayers(this.level.players());
|
|
||||||
+ playerchunkmap_entitytracker.updatePlayers(entity.getPlayersInTrackRange()); // Paper - don't search all players
|
|
||||||
if (entity instanceof ServerPlayer) {
|
|
||||||
ServerPlayer entityplayer = (ServerPlayer) entity;
|
|
||||||
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
entity.tracker = null; // Paper - We're no longer tracked
|
|
||||||
}
|
|
||||||
|
|
||||||
+ // Paper start - optimised tracker
|
|
||||||
+ private final void processTrackQueue() {
|
|
||||||
+ this.level.timings.tracker1.startTiming();
|
|
||||||
+ try {
|
|
||||||
+ for (TrackedEntity tracker : this.entityMap.values()) {
|
|
||||||
+ // update tracker entry
|
|
||||||
+ tracker.updatePlayers(tracker.entity.getPlayersInTrackRange());
|
|
||||||
+ }
|
|
||||||
+ } finally {
|
|
||||||
+ this.level.timings.tracker1.stopTiming();
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ this.level.timings.tracker2.startTiming();
|
|
||||||
+ try {
|
|
||||||
+ for (TrackedEntity tracker : this.entityMap.values()) {
|
|
||||||
+ tracker.serverEntity.sendChanges();
|
|
||||||
+ }
|
|
||||||
+ } finally {
|
|
||||||
+ this.level.timings.tracker2.stopTiming();
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimised tracker
|
|
||||||
+
|
|
||||||
protected void tick() {
|
|
||||||
+ // Paper start - optimized tracker
|
|
||||||
+ if (true) {
|
|
||||||
+ this.processTrackQueue();
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimized tracker
|
|
||||||
List<ServerPlayer> list = Lists.newArrayList();
|
|
||||||
List<ServerPlayer> list1 = this.level.players();
|
|
||||||
ObjectIterator objectiterator = this.entityMap.values().iterator();
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
}));
|
|
||||||
// Paper end
|
|
||||||
DebugPackets.sendPoiPacketsForChunk(this.level, chunk.getPos());
|
|
||||||
- List<Entity> list = Lists.newArrayList();
|
|
||||||
- List<Entity> list1 = Lists.newArrayList();
|
|
||||||
- ObjectIterator objectiterator = this.entityMap.values().iterator();
|
|
||||||
-
|
|
||||||
- while (objectiterator.hasNext()) {
|
|
||||||
- ChunkMap.TrackedEntity playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next();
|
|
||||||
- Entity entity = playerchunkmap_entitytracker.entity;
|
|
||||||
-
|
|
||||||
- if (entity != player && entity.chunkPosition().equals(chunk.getPos())) {
|
|
||||||
- playerchunkmap_entitytracker.updatePlayer(player);
|
|
||||||
- if (entity instanceof Mob && ((Mob) entity).getLeashHolder() != null) {
|
|
||||||
- list.add(entity);
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- if (!entity.getPassengers().isEmpty()) {
|
|
||||||
- list1.add(entity);
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- Iterator iterator;
|
|
||||||
- Entity entity1;
|
|
||||||
-
|
|
||||||
- if (!list.isEmpty()) {
|
|
||||||
- iterator = list.iterator();
|
|
||||||
-
|
|
||||||
- while (iterator.hasNext()) {
|
|
||||||
- entity1 = (Entity) iterator.next();
|
|
||||||
- player.connection.send(new ClientboundSetEntityLinkPacket(entity1, ((Mob) entity1).getLeashHolder()));
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- if (!list1.isEmpty()) {
|
|
||||||
- iterator = list1.iterator();
|
|
||||||
-
|
|
||||||
- while (iterator.hasNext()) {
|
|
||||||
- entity1 = (Entity) iterator.next();
|
|
||||||
- player.connection.send(new ClientboundSetPassengersPacket(entity1));
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
+ // Paper - no longer needed - this was used to account for clients bugging out since they needed a chunk to store entities, but they no longer need a chunk
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
||||||
this.lastSectionPos = SectionPos.of((EntityAccess) entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
+ // Paper start - use distance map to optimise tracker
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> lastTrackerCandidates;
|
|
||||||
+
|
|
||||||
+ final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newTrackerCandidates) {
|
|
||||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> oldTrackerCandidates = this.lastTrackerCandidates;
|
|
||||||
+ this.lastTrackerCandidates = newTrackerCandidates;
|
|
||||||
+
|
|
||||||
+ if (newTrackerCandidates != null) {
|
|
||||||
+ Object[] rawData = newTrackerCandidates.getBackingSet();
|
|
||||||
+ for (int i = 0, len = rawData.length; i < len; ++i) {
|
|
||||||
+ Object raw = rawData[i];
|
|
||||||
+ if (!(raw instanceof ServerPlayer)) {
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ ServerPlayer player = (ServerPlayer)raw;
|
|
||||||
+ this.updatePlayer(player);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (oldTrackerCandidates == newTrackerCandidates) {
|
|
||||||
+ // this is likely the case.
|
|
||||||
+ // means there has been no range changes, so we can just use the above for tracking.
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // stuff could have been removed, so we need to check the trackedPlayers set
|
|
||||||
+ // for players that were removed
|
|
||||||
+
|
|
||||||
+ for (ServerPlayerConnection conn : this.seenBy.toArray(new ServerPlayerConnection[0])) { // avoid CME
|
|
||||||
+ if (newTrackerCandidates == null || !newTrackerCandidates.contains(conn.getPlayer())) {
|
|
||||||
+ this.updatePlayer(conn.getPlayer());
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ // Paper end - use distance map to optimise tracker
|
|
||||||
+
|
|
||||||
public boolean equals(Object object) {
|
|
||||||
return object instanceof ChunkMap.TrackedEntity ? ((ChunkMap.TrackedEntity) object).entity.getId() == this.entity.getId() : false;
|
|
||||||
}
|
|
||||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
||||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
||||||
@@ -0,0 +0,0 @@ import net.minecraft.network.syncher.EntityDataSerializers;
|
|
||||||
import net.minecraft.network.syncher.SynchedEntityData;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
+import io.papermc.paper.util.MCUtil;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
|
||||||
|
|
||||||
public boolean updatingSectionStatus = false;
|
|
||||||
// Paper end
|
|
||||||
+ // Paper start - optimise entity tracking
|
|
||||||
+ final org.spigotmc.TrackingRange.TrackingRangeType trackingRangeType = org.spigotmc.TrackingRange.getTrackingRangeType(this);
|
|
||||||
+
|
|
||||||
+ public boolean isLegacyTrackingEntity = false;
|
|
||||||
+
|
|
||||||
+ public final void setLegacyTrackingEntity(final boolean isLegacyTrackingEntity) {
|
|
||||||
+ this.isLegacyTrackingEntity = isLegacyTrackingEntity;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> getPlayersInTrackRange() {
|
|
||||||
+ // determine highest range of passengers
|
|
||||||
+ if (this.passengers.isEmpty()) {
|
|
||||||
+ return ((ServerLevel)this.level).getChunkSource().chunkMap.playerEntityTrackerTrackMaps[this.trackingRangeType.ordinal()]
|
|
||||||
+ .getObjectsInRange(MCUtil.getCoordinateKey(this));
|
|
||||||
+ }
|
|
||||||
+ Iterable<Entity> passengers = this.getIndirectPassengers();
|
|
||||||
+ net.minecraft.server.level.ChunkMap chunkMap = ((ServerLevel)this.level).getChunkSource().chunkMap;
|
|
||||||
+ org.spigotmc.TrackingRange.TrackingRangeType type = this.trackingRangeType;
|
|
||||||
+ int range = chunkMap.getEntityTrackerRange(type.ordinal());
|
|
||||||
+
|
|
||||||
+ for (Entity passenger : passengers) {
|
|
||||||
+ org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType;
|
|
||||||
+ int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal());
|
|
||||||
+ if (passengerRange > range) {
|
|
||||||
+ type = passengerType;
|
|
||||||
+ range = passengerRange;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return chunkMap.playerEntityTrackerTrackMaps[type.ordinal()].getObjectsInRange(MCUtil.getCoordinateKey(this));
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimise entity tracking
|
|
||||||
|
|
||||||
public Entity(EntityType<?> type, Level world) {
|
|
||||||
this.id = Entity.ENTITY_COUNTER.incrementAndGet();
|
|
||||||
diff --git a/src/main/java/org/spigotmc/TrackingRange.java b/src/main/java/org/spigotmc/TrackingRange.java
|
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
||||||
--- a/src/main/java/org/spigotmc/TrackingRange.java
|
|
||||||
+++ b/src/main/java/org/spigotmc/TrackingRange.java
|
|
||||||
@@ -0,0 +0,0 @@ public class TrackingRange
|
|
||||||
return config.otherTrackingRange;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ // Paper start - optimise entity tracking
|
|
||||||
+ // copied from above, TODO check on update
|
|
||||||
+ public static TrackingRangeType getTrackingRangeType(Entity entity)
|
|
||||||
+ {
|
|
||||||
+ if (entity instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon) return TrackingRangeType.ENDERDRAGON; // Paper - enderdragon is exempt
|
|
||||||
+ if ( entity instanceof ServerPlayer )
|
|
||||||
+ {
|
|
||||||
+ return TrackingRangeType.PLAYER;
|
|
||||||
+ // Paper start - Simplify and set water mobs to animal tracking range
|
|
||||||
+ }
|
|
||||||
+ switch (entity.activationType) {
|
|
||||||
+ case RAIDER:
|
|
||||||
+ case MONSTER:
|
|
||||||
+ case FLYING_MONSTER:
|
|
||||||
+ return TrackingRangeType.MONSTER;
|
|
||||||
+ case WATER:
|
|
||||||
+ case VILLAGER:
|
|
||||||
+ case ANIMAL:
|
|
||||||
+ return TrackingRangeType.ANIMAL;
|
|
||||||
+ case MISC:
|
|
||||||
+ }
|
|
||||||
+ if ( entity instanceof ItemFrame || entity instanceof Painting || entity instanceof ItemEntity || entity instanceof ExperienceOrb )
|
|
||||||
+ // Paper end
|
|
||||||
+ {
|
|
||||||
+ return TrackingRangeType.MISC;
|
|
||||||
+ } else if (entity instanceof Display) {
|
|
||||||
+ return TrackingRangeType.DISPLAY;
|
|
||||||
+ } else
|
|
||||||
+ {
|
|
||||||
+ return TrackingRangeType.OTHER;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public static enum TrackingRangeType {
|
|
||||||
+ PLAYER,
|
|
||||||
+ ANIMAL,
|
|
||||||
+ MONSTER,
|
|
||||||
+ MISC,
|
|
||||||
+ OTHER,
|
|
||||||
+ ENDERDRAGON,
|
|
||||||
+ DISPLAY;
|
|
||||||
+ }
|
|
||||||
+ // Paper end - optimise entity tracking
|
|
||||||
}
|
|
Loading…
Reference in a new issue