1.18 misc performance dev branch (#7368)

- Port player chunk loader patch
Makes the chunk system act as it did in 1.17, no additional tickets (and thus logic) to make a chunk ticking.
Adds simulation distance API, deprecates old no-tick method.
- More collision optimisations
Ancient patch from tuinity that never could be pushed to master.
- Fix Optimise ArraySetSorted#removeIf patch
- Execute chunk tasks fairly for worlds while waiting for next tick
- Port Replace ticket level propagator
This commit is contained in:
Spottedleaf 2022-02-18 09:44:46 -08:00 committed by GitHub
parent b173c3ee2c
commit 286bd1bfb5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 1838 additions and 743 deletions

View file

@ -8,10 +8,10 @@ Add per player no-tick, tick, and send view distances.
Also add send/no-tick view distance to World.
diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
index cf6fe1b5a1531e8d30c0386e36c023d003458b7e..bf23ef001fb5177b7aab0b3ed8752f58641bb840 100644
index cf6fe1b5a1531e8d30c0386e36c023d003458b7e..ad342ecd8b86903276c62644624cff55cf190026 100644
--- a/src/main/java/org/bukkit/World.java
+++ b/src/main/java/org/bukkit/World.java
@@ -2597,6 +2597,52 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
@@ -2597,6 +2597,62 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
int getSimulationDistance();
// Spigot end
@ -23,13 +23,21 @@ index cf6fe1b5a1531e8d30c0386e36c023d003458b7e..bf23ef001fb5177b7aab0b3ed8752f58
+ void setViewDistance(int viewDistance);
+
+ /**
+ * Sets the simulation distance for this world.
+ * @param simulationDistance simulation distance in [2, 32]
+ */
+ void setSimulationDistance(int simulationDistance);
+
+ /**
+ * Returns the no-tick view distance for this world.
+ * <p>
+ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not
+ * be set to tick.
+ * </p>
+ * @return The no-tick view distance for this world.
+ * @deprecated Use {@link #getViewDistance()}
+ */
+ @Deprecated
+ int getNoTickViewDistance();
+
+ /**
@ -39,7 +47,9 @@ index cf6fe1b5a1531e8d30c0386e36c023d003458b7e..bf23ef001fb5177b7aab0b3ed8752f58
+ * be set to tick.
+ * </p>
+ * @param viewDistance view distance in [2, 32]
+ * @deprecated Use {@link #setViewDistance(int)}
+ */
+ @Deprecated
+ void setNoTickViewDistance(int viewDistance);
+
+ /**
@ -49,7 +59,7 @@ index cf6fe1b5a1531e8d30c0386e36c023d003458b7e..bf23ef001fb5177b7aab0b3ed8752f58
+ * </p>
+ * @return The sending view distance for this world.
+ */
+ public int getSendViewDistance();
+ int getSendViewDistance();
+
+ /**
+ * Sets the sending view distance for this world.
@ -58,17 +68,17 @@ index cf6fe1b5a1531e8d30c0386e36c023d003458b7e..bf23ef001fb5177b7aab0b3ed8752f58
+ * </p>
+ * @param viewDistance view distance in [2, 32] or -1
+ */
+ public void setSendViewDistance(int viewDistance);
+ void setSendViewDistance(int viewDistance);
+ // Paper end - view distance api
+
// Spigot start
public class Spigot {
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 43d91f578b0aefd18f7f5ecd120a366af4ee98e6..2d93f5ad7f9c0df08bcd099a813c1d8e9b8c16eb 100644
index 43d91f578b0aefd18f7f5ecd120a366af4ee98e6..9f861fcc350b803e63b08631f7a6c30d13b4cb7c 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -1793,6 +1793,62 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@@ -1793,6 +1793,78 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
* @param affects Whether the player can affect mob spawning
*/
public void setAffectsSpawning(boolean affects);
@ -78,7 +88,6 @@ index 43d91f578b0aefd18f7f5ecd120a366af4ee98e6..2d93f5ad7f9c0df08bcd099a813c1d8e
+ *
+ * @return the player's view distance
+ * @see org.bukkit.World#getViewDistance()
+ * @see org.bukkit.World#getNoTickViewDistance()
+ */
+ public int getViewDistance();
+
@ -87,18 +96,33 @@ index 43d91f578b0aefd18f7f5ecd120a366af4ee98e6..2d93f5ad7f9c0df08bcd099a813c1d8e
+ *
+ * @param viewDistance the player's view distance
+ * @see org.bukkit.World#setViewDistance(int)
+ * @see org.bukkit.World#setNoTickViewDistance(int)
+ */
+ public void setViewDistance(int viewDistance);
+
+ /**
+ * Gets the simulation distance for this player
+ *
+ * @return the player's simulation distance
+ */
+ public int getSimulationDistance();
+
+ /**
+ * Sets the simulation distance for this player
+ *
+ * @param simulationDistance the player's new simulation distance
+ */
+ public void setSimulationDistance(int simulationDistance);
+
+ /**
+ * Gets the no-ticking view distance for this player.
+ * <p>
+ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not
+ * be set to tick.
+ * </p>
+ * @return The no-tick view distance for this player.
+ * @deprecated Use {@link #getViewDistance()}
+ */
+ @Deprecated
+ public int getNoTickViewDistance();
+
+ /**
@ -108,7 +132,9 @@ index 43d91f578b0aefd18f7f5ecd120a366af4ee98e6..2d93f5ad7f9c0df08bcd099a813c1d8e
+ * be set to tick.
+ * </p>
+ * @param viewDistance view distance in [2, 32] or -1
+ * @deprecated Use {@link #setViewDistance(int)}
+ */
+ @Deprecated
+ public void setNoTickViewDistance(int viewDistance);
+
+ /**

View file

@ -5,7 +5,7 @@ Subject: [PATCH] Complete resource pack API
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 5f5cde854dd7928e0b640ab1713f42194eab0833..09b5941bcd412420ffc6edfe62bc95cff426ba06 100644
index 29bd571bd270b9c6ed05d95122d42d9bc0c797f6..fd8a40bacaaa39c20428bbebe8611ba8cdd33e0b 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -1284,7 +1284,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@ -18,7 +18,7 @@ index 5f5cde854dd7928e0b640ab1713f42194eab0833..09b5941bcd412420ffc6edfe62bc95cf
public void setResourcePack(@NotNull String url);
/**
@@ -2058,6 +2060,124 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@@ -2074,6 +2076,124 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
default net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowEntity> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowEntity> op) {
return net.kyori.adventure.text.event.HoverEvent.showEntity(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowEntity.of(this.getType().getKey(), this.getUniqueId(), this.displayName())));
}

View file

@ -5,7 +5,7 @@ Subject: [PATCH] Add String based Action Bar API
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 09b5941bcd412420ffc6edfe62bc95cff426ba06..a0777f9dc7cb6d4274635d794cf66de714535cde 100644
index fd8a40bacaaa39c20428bbebe8611ba8cdd33e0b..d6a2e66e07590013bf1596012a96ec1018493e06 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -3,6 +3,7 @@ package org.bukkit.entity;
@ -68,7 +68,7 @@ index 09b5941bcd412420ffc6edfe62bc95cff426ba06..a0777f9dc7cb6d4274635d794cf66de7
public default void sendMessage(net.md_5.bungee.api.ChatMessageType position, net.md_5.bungee.api.chat.BaseComponent... components) {
spigot().sendMessage(position, components);
}
@@ -2249,6 +2285,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@@ -2265,6 +2301,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
/**
* Sends the component to the specified screen position of this player
*
@ -76,7 +76,7 @@ index 09b5941bcd412420ffc6edfe62bc95cff426ba06..a0777f9dc7cb6d4274635d794cf66de7
* @param position the screen position
* @param component the components to send
* @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
@@ -2261,6 +2298,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@@ -2277,6 +2314,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
/**
* Sends an array of components as a single message to the specified screen position of this player
*

View file

@ -62,10 +62,10 @@ index 51c96a0b6645cf31f4ca051f6a8c75b5f188484c..80d474a979add473c99692ccde93439d
/**
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index b68f2774e4f88e905cc195df6d1592d96103df7a..23df52c7ac4ec5e687e763c13a0937c3fbc5dd47 100644
index c16716e77427d0968c004a5b30600a40a36d5137..888a60fe60e6242680c153fd8edd95f6e37e26d5 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -2235,6 +2235,20 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@@ -2251,6 +2251,20 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
* was {@link org.bukkit.event.player.PlayerResourcePackStatusEvent.Status#SUCCESSFULLY_LOADED}
*/
boolean hasResourcePack();

View file

@ -24,10 +24,10 @@ index 8a479c7dfd3825fab8bb057d8afa5ae0cb01b071..6ef0d7f3dcb779fb7dc5786e74332620
/**
* Make the entity drop the item in their hand.
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 23df52c7ac4ec5e687e763c13a0937c3fbc5dd47..fd4613f702ebbd32ec22a81f993a1ea9d8dd896f 100644
index 888a60fe60e6242680c153fd8edd95f6e37e26d5..87f62e65dbf8128d684abaf4fde88cfa566f7a1f 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -2090,7 +2090,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@@ -2106,7 +2106,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
/**
* Open a Sign for editing by the Player.
*

View file

@ -5,10 +5,10 @@ Subject: [PATCH] Expose attack cooldown methods for Player
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 61e75620b205cfeda0aee433651c45235bf21181..11e85e664fdd875c2a6e84b158b78d4784999932 100644
index 81b3d2da2bb92c874845688a8b8a2b15b2a4bd59..ffbc26048e359044be6b0c96f3bd8cd1048fb316 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -2405,6 +2405,26 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@@ -2421,6 +2421,26 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
* @param profile The new profile to use
*/
void setPlayerProfile(@NotNull com.destroystokyo.paper.profile.PlayerProfile profile);

View file

@ -193,7 +193,7 @@ index 0000000000000000000000000000000000000000..f7f171c4ee0b8339b2f8fbe82442d65f
+ }
+}
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 11e85e664fdd875c2a6e84b158b78d4784999932..385846a2011ec07d4f37c98f38d3369199780418 100644
index ffbc26048e359044be6b0c96f3bd8cd1048fb316..db12ce34a7fa4f309e7cc52c953bb409e9b6ab0c 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -2,6 +2,7 @@ package org.bukkit.entity;
@ -204,7 +204,7 @@ index 11e85e664fdd875c2a6e84b158b78d4784999932..385846a2011ec07d4f37c98f38d33691
import com.destroystokyo.paper.Title; // Paper
import net.kyori.adventure.text.Component;
import org.bukkit.DyeColor;
@@ -2425,6 +2426,12 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@@ -2441,6 +2442,12 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
* Reset the cooldown counter to 0, effectively starting the cooldown period.
*/
void resetCooldown();

View file

@ -5,10 +5,10 @@ Subject: [PATCH] Brand support
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 385846a2011ec07d4f37c98f38d3369199780418..e61d0ff51674213e4711d5bbe9e8aaed31ed00df 100644
index db12ce34a7fa4f309e7cc52c953bb409e9b6ab0c..6983b3d6d5402ea1ed3646bffdd4d8bc613a6121 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -2560,6 +2560,16 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@@ -2576,6 +2576,16 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
// Paper end
}

View file

@ -5,10 +5,10 @@ Subject: [PATCH] Player elytra boost API
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index e61d0ff51674213e4711d5bbe9e8aaed31ed00df..45284317f195f08e88a4977a32a1757afb6c4b17 100644
index 6983b3d6d5402ea1ed3646bffdd4d8bc613a6121..167ea1fe8d5d16098559745a67e5128e4ef78082 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -2432,6 +2432,19 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@@ -2448,6 +2448,19 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
*/
@NotNull
<T> T getClientOption(@NotNull ClientOption<T> option);

View file

@ -5,10 +5,10 @@ Subject: [PATCH] Add sendOpLevel API
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 45284317f195f08e88a4977a32a1757afb6c4b17..71de9f4c7f07c4c0b1155df14794de3ba8e28d69 100644
index 167ea1fe8d5d16098559745a67e5128e4ef78082..6efcd4c5b89af422c630a566e8a851062e05b69a 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -2445,6 +2445,17 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@@ -2461,6 +2461,17 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
*/
@Nullable
Firework boostElytra(@NotNull ItemStack firework);

View file

@ -298,7 +298,7 @@ index 0000000000000000000000000000000000000000..bee2fa2bfbb61209381f24ed6508d3d1
+}
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..e4368db074da7b5e48b47d41875c1e63b9745c2a
index 0000000000000000000000000000000000000000..5c7b2850d311e4d54236ba9acce148bca05672fd
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
@@ -0,0 +1,188 @@
@ -364,8 +364,8 @@ index 0000000000000000000000000000000000000000..e4368db074da7b5e48b47d41875c1e63
+ commands = new HashMap<String, Command>();
+ commands.put("paper", new PaperCommand("paper"));
+
+ version = getInt("config-version", 24);
+ set("config-version", 24);
+ version = getInt("config-version", 25);
+ set("config-version", 25);
+ readConfig(PaperConfig.class, null);
+ }
+

View file

@ -75,7 +75,7 @@ index 0000000000000000000000000000000000000000..d0211d4f39f9d6af1d751ac66342b42c
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
index e683e5bf47abe7bd3d2f7e9811a377549308ded4..c20fe23174d8a12bfc5acb4b0e947c6fca5ab897 100644
index a3c3656ba4e8bb85bfb3186d6284f02f09975b13..6cfaef1886b517c56ed9a96347be6e51efa84d9f 100644
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
@@ -69,6 +69,7 @@ public class PaperConfig {
@ -84,8 +84,8 @@ index e683e5bf47abe7bd3d2f7e9811a377549308ded4..c20fe23174d8a12bfc5acb4b0e947c6f
commands.put("paper", new PaperCommand("paper"));
+ commands.put("mspt", new MSPTCommand("mspt"));
version = getInt("config-version", 24);
set("config-version", 24);
version = getInt("config-version", 25);
set("config-version", 25);
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 76da16590f27702883c07200a02db823d9720c61..3c2af39f7bd62448a3075d327132ebc19af6bd77 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java

View file

@ -5,6 +5,41 @@ Subject: [PATCH] Optimise ArraySetSorted#removeIf
Remove iterator allocation and ensure the call is always O(n)
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
index 07b616d9d7cde77c001f5c627daef0731d883e61..e8b4de332cc655a55621e40360fa46d893d903f4 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
@@ -85,13 +85,27 @@ public abstract class DistanceManager {
protected void purgeStaleTickets() {
++this.ticketTickCounter;
ObjectIterator objectiterator = this.tickets.long2ObjectEntrySet().fastIterator();
+ // Paper start - use optimised removeIf
+ long[] currChunk = new long[1];
+ long ticketCounter = DistanceManager.this.ticketTickCounter;
+ java.util.function.Predicate<Ticket<?>> removeIf = (ticket) -> {
+ final boolean ret = ticket.timedOut(ticketCounter);
+ if (ret) {
+ this.tickingTicketsTracker.removeTicket(currChunk[0], ticket);
+ }
+ return ret;
+ };
+ // Paper end - use optimised removeIf
while (objectiterator.hasNext()) {
Entry<SortedArraySet<Ticket<?>>> entry = (Entry) objectiterator.next();
- Iterator<Ticket<?>> iterator = ((SortedArraySet) entry.getValue()).iterator();
- boolean flag = false;
+ // Paper start - use optimised removeIf
+ Iterator<Ticket<?>> iterator = null;
+ currChunk[0] = entry.getLongKey();
+ boolean flag = entry.getValue().removeIf(removeIf);
- while (iterator.hasNext()) {
+ while (false && iterator.hasNext()) {
+ // Paper end - use optimised removeIf
Ticket<?> ticket = (Ticket) iterator.next();
if (ticket.timedOut(this.ticketTickCounter)) {
diff --git a/src/main/java/net/minecraft/util/SortedArraySet.java b/src/main/java/net/minecraft/util/SortedArraySet.java
index d1b2ba24ef54e01c6249c3b2ca16e80f03c001a6..5f1c4c6b9e36f2d6ec43b82cc0e2cae24b800dc4 100644
--- a/src/main/java/net/minecraft/util/SortedArraySet.java

View file

@ -7,10 +7,10 @@ Subject: [PATCH] Don't crash if player is attempted to be removed from
I suspect it deals with teleporting as it uses players current x/y/z
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
index f0dac1f596911eb2109192ef16a619f8ae71d1f7..8868ffcda194e8c2300181a2cdda9337dbde6284 100644
index e8b4de332cc655a55621e40360fa46d893d903f4..75f11490737018096e7645ce0b991fd210c8f596 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
@@ -283,8 +283,8 @@ public abstract class DistanceManager {
@@ -297,8 +297,8 @@ public abstract class DistanceManager {
ObjectSet<ServerPlayer> objectset = (ObjectSet) this.playersPerChunk.get(i);
if (objectset == null) return; // CraftBukkit - SPIGOT-6208

View file

@ -6,10 +6,10 @@ 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 74d674b2684b0db4aa6c183edc6091d53e9ee882..626bcbc6dd013260c3f8b38a1d14e7ba35dc1e01 100644
index 74d674b2684b0db4aa6c183edc6091d53e9ee882..560cc04ee7a8fd0c3c7217e08d53d1da682054e2 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -73,6 +73,18 @@ public class ChunkHolder {
@@ -73,6 +73,23 @@ public class ChunkHolder {
boolean isUpdateQueued = false; // Paper
private final ChunkMap chunkMap; // Paper
@ -18,26 +18,31 @@ index 74d674b2684b0db4aa6c183edc6091d53e9ee882..626bcbc6dd013260c3f8b38a1d14e7ba
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInMobSpawnRange;
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInChunkTickRange;
+
+ void updateRanges() {
+ void onChunkAdd() {
+ long key = net.minecraft.server.MCUtil.getCoordinateKey(this.pos);
+ this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
+ this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
+ }
+
+ void onChunkRemove() {
+ this.playersInMobSpawnRange = null;
+ this.playersInChunkTickRange = null;
+ }
+ // Paper end - optimise anyPlayerCloseEnoughForSpawning
+
public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) {
this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size());
this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
@@ -94,6 +106,7 @@ public class ChunkHolder {
@@ -94,6 +111,7 @@ public class ChunkHolder {
this.setTicketLevel(level);
this.changedBlocksPerSection = new ShortSet[world.getSectionsCount()];
this.chunkMap = (ChunkMap)playersWatchingChunkProvider; // Paper
+ this.updateRanges(); // Paper - optimise anyPlayerCloseEnoughForSpawning
+ this.onChunkAdd(); // Paper - optimise anyPlayerCloseEnoughForSpawning
}
// CraftBukkit start
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 7f8c82f2f50934bcc2eacd40a5fbd93d5dae68f6..745b8c16600ba106907302daa9630abdce6807b0 100644
index 00eeaa04c98a658bac63f72da6b6d157131e9120..cd58121bfa059366919e098005299dde0c55f46f 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -177,11 +177,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@ -122,15 +127,23 @@ index 7f8c82f2f50934bcc2eacd40a5fbd93d5dae68f6..745b8c16600ba106907302daa9630abd
}
protected ChunkGenerator generator() {
@@ -487,6 +536,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} else {
if (holder != null) {
holder.setTicketLevel(level);
+ holder.updateRanges(); // Paper - optimise anyPlayerCloseEnoughForSpawning
}
@@ -501,6 +550,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
holder = (ChunkHolder) this.pendingUnloads.remove(pos);
if (holder != null) {
holder.setTicketLevel(level);
+ holder.onChunkAdd(); // Paper - optimise anyPlayerCloseEnoughForSpawning - PUT HERE AFTER RE-ADDING ONLY
} else {
holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this.queueSorter, this);
// Paper start
@@ -600,6 +650,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
ChunkHolder playerchunk = (ChunkHolder) this.updatingChunkMap.remove(j);
if (holder != null) {
@@ -1331,43 +1381,48 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (playerchunk != null) {
+ playerchunk.onChunkRemove(); // Paper
this.pendingUnloads.put(j, playerchunk);
this.modified = true;
this.scheduleUnload(j, playerchunk); // Paper - Move up - don't leak chunks
@@ -1331,43 +1382,48 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return this.anyPlayerCloseEnoughForSpawning(pos, false);
}
@ -211,7 +224,7 @@ index 7f8c82f2f50934bcc2eacd40a5fbd93d5dae68f6..745b8c16600ba106907302daa9630abd
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 f11e9421d393ef5a04f24c78a1214359710cc2f8..3865146697051e01a778414064425ea0e5162b8d 100644
index 75f11490737018096e7645ce0b991fd210c8f596..14774d51dd35a98471d29230a64fadee74651d19 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
@@ -48,7 +48,7 @@ public abstract class DistanceManager {
@ -223,7 +236,7 @@ index f11e9421d393ef5a04f24c78a1214359710cc2f8..3865146697051e01a778414064425ea0
private final TickingTracker tickingTicketsTracker = new TickingTracker();
private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(33);
// Paper start use a queue, but still keep unique requirement
@@ -125,7 +125,7 @@ public abstract class DistanceManager {
@@ -139,7 +139,7 @@ public abstract class DistanceManager {
protected abstract ChunkHolder updateChunkScheduling(long pos, int level, @Nullable ChunkHolder holder, int k);
public boolean runAllUpdates(ChunkMap chunkStorage) {
@ -232,7 +245,7 @@ index f11e9421d393ef5a04f24c78a1214359710cc2f8..3865146697051e01a778414064425ea0
this.tickingTicketsTracker.runAllUpdates();
this.playerTicketManager.runAllUpdates();
int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE);
@@ -272,7 +272,7 @@ public abstract class DistanceManager {
@@ -286,7 +286,7 @@ public abstract class DistanceManager {
((ObjectSet) this.playersPerChunk.computeIfAbsent(i, (j) -> {
return new ObjectOpenHashSet();
})).add(player);
@ -241,7 +254,7 @@ index f11e9421d393ef5a04f24c78a1214359710cc2f8..3865146697051e01a778414064425ea0
this.playerTicketManager.update(i, 0, true);
this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair);
}
@@ -286,7 +286,7 @@ public abstract class DistanceManager {
@@ -300,7 +300,7 @@ 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);
@ -250,7 +263,7 @@ index f11e9421d393ef5a04f24c78a1214359710cc2f8..3865146697051e01a778414064425ea0
this.playerTicketManager.update(i, Integer.MAX_VALUE, false);
this.tickingTicketsTracker.removeTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair);
}
@@ -330,13 +330,17 @@ public abstract class DistanceManager {
@@ -344,13 +344,17 @@ public abstract class DistanceManager {
// Paper end
public int getNaturalSpawnChunkCount() {
@ -273,7 +286,7 @@ index f11e9421d393ef5a04f24c78a1214359710cc2f8..3865146697051e01a778414064425ea0
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 02516d4a6ee08908765f5bc84b14560754a67680..b327bd2f48166a4a0c831b0209695fe5935f6a68 100644
index 8cfd38617b49ea6be318abc819f2ed5b7dabd2e1..59cd2b8ecdd2c962a027526fe45032559aa2ee8c 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -881,6 +881,37 @@ public class ServerChunkCache extends ChunkSource {

View file

@ -6,7 +6,7 @@ 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 d924dfe452244633bcf62e7ccefe3a5a1a3d7f37..e35a47e0c8eebbad7154a1357a6b868887bc4a0c 100644
index cd58121bfa059366919e098005299dde0c55f46f..25c71ca93c54976cc319abb0d932beb6f95deb79 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -67,6 +67,7 @@ import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket;
@ -126,7 +126,7 @@ index d924dfe452244633bcf62e7ccefe3a5a1a3d7f37..e35a47e0c8eebbad7154a1357a6b8688
// 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,
@@ -1504,17 +1582,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1505,17 +1583,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void move(ServerPlayer player) {
@ -145,7 +145,7 @@ index d924dfe452244633bcf62e7ccefe3a5a1a3d7f37..e35a47e0c8eebbad7154a1357a6b8688
int i = SectionPos.blockToSectionCoord(player.getBlockX());
int j = SectionPos.blockToSectionCoord(player.getBlockZ());
@@ -1641,7 +1709,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1642,7 +1710,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.tracker = playerchunkmap_entitytracker; // Paper - Fast access to tracker
this.entityMap.put(entity.getId(), playerchunkmap_entitytracker);
@ -154,7 +154,7 @@ index d924dfe452244633bcf62e7ccefe3a5a1a3d7f37..e35a47e0c8eebbad7154a1357a6b8688
if (entity instanceof ServerPlayer) {
ServerPlayer entityplayer = (ServerPlayer) entity;
@@ -1685,7 +1753,37 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1686,7 +1754,37 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.tracker = null; // Paper - We're no longer tracked
}
@ -192,7 +192,7 @@ index d924dfe452244633bcf62e7ccefe3a5a1a3d7f37..e35a47e0c8eebbad7154a1357a6b8688
List<ServerPlayer> list = Lists.newArrayList();
List<ServerPlayer> list1 = this.level.players();
ObjectIterator objectiterator = this.entityMap.values().iterator();
@@ -1761,23 +1859,31 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1762,23 +1860,31 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
DebugPackets.sendPoiPacketsForChunk(this.level, chunk.getPos());
List<Entity> list = Lists.newArrayList();
List<Entity> list1 = Lists.newArrayList();
@ -236,7 +236,7 @@ index d924dfe452244633bcf62e7ccefe3a5a1a3d7f37..e35a47e0c8eebbad7154a1357a6b8688
Iterator iterator;
Entity entity1;
@@ -1853,6 +1959,42 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1854,6 +1960,42 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.lastSectionPos = SectionPos.of(entity);
}
@ -280,7 +280,7 @@ index d924dfe452244633bcf62e7ccefe3a5a1a3d7f37..e35a47e0c8eebbad7154a1357a6b8688
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 a02eb37845e0609ddf14a4214395e00443534b08..8e59a63e8c161e90ccff951ecb2b6530fadf3b80 100644
index 7bf62752b6604abe0bda6f5d0024f0e93efb3a9a..9a4454746452f64d7a8caa52eef2dca86e9edd94 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -51,6 +51,7 @@ import net.minecraft.network.syncher.EntityDataSerializers;

View file

@ -35,10 +35,10 @@ index bc880a78381b9c30e4ec92bdf17c9eca1ded41f2..eae27a641ee69f3383f1200f8c8ab8dd
+ }
}
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
index 3865146697051e01a778414064425ea0e5162b8d..cd81e7844c985d7d0f0930faa96478af6a7f6cd4 100644
index 14774d51dd35a98471d29230a64fadee74651d19..93a642cd6cf21a268074a73bff22249d32992ad3 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
@@ -197,6 +197,27 @@ public abstract class DistanceManager {
@@ -211,6 +211,27 @@ public abstract class DistanceManager {
boolean removed = false; // CraftBukkit
if (arraysetsorted.remove(ticket)) {
removed = true; // CraftBukkit

View file

@ -5,7 +5,7 @@ Subject: [PATCH] incremental chunk and player saving
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
index a545a882335094daa787ec5c7005939080195263..933d3ace21f5a313f1d5e4dfd86777f6fa235f3f 100644
index df1337c89c943a80a31d127c844f061c1e56eb91..cb967380155d34ff511346d54b61e1c2109780f3 100644
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
@@ -459,4 +459,14 @@ public class PaperConfig {
@ -24,7 +24,7 @@ index a545a882335094daa787ec5c7005939080195263..933d3ace21f5a313f1d5e4dfd86777f6
+ }
}
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index d6d549df6e8920c936dd0d1b7ba828dbebc60b32..8b4b521a84c8623665d21d0340bca7665953d20b 100644
index eae27a641ee69f3383f1200f8c8ab8dd7a4105a4..12a6f12ffa8a45b054b01a7edc73ca8a5bcb59b5 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -64,6 +64,21 @@ public class PaperWorldConfig {
@ -50,7 +50,7 @@ index d6d549df6e8920c936dd0d1b7ba828dbebc60b32..8b4b521a84c8623665d21d0340bca766
config.addDefault("world-settings.default." + path, def);
return config.getBoolean("world-settings." + worldName + "." + path, config.getBoolean("world-settings.default." + path));
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 918fdf14080338983b8725bf2619088fd23c332a..95842327aa08d4717f86e9dcc0519ab24c41ca14 100644
index 9a926114aa550ca5a6329e3ce993bf1f686cd10e..ab7ca991e5d7940a386a820e1953df1ac2428107 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -902,7 +902,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@ -98,11 +98,11 @@ index 918fdf14080338983b8725bf2619088fd23c332a..95842327aa08d4717f86e9dcc0519ab2
// Paper start - move executeAll() into full server tick timing
try (co.aikar.timings.Timing ignored = MinecraftTimings.processTasksTimer.startTiming()) {
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index 626bcbc6dd013260c3f8b38a1d14e7ba35dc1e01..9e96b0465717bfa761289c255fd8d2f1df1be3d8 100644
index 560cc04ee7a8fd0c3c7217e08d53d1da682054e2..5479deb3f80a95c58cb6f0f532b2f56b71a68a3a 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -84,6 +84,8 @@ public class ChunkHolder {
this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
@@ -89,6 +89,8 @@ public class ChunkHolder {
this.playersInChunkTickRange = null;
}
// Paper end - optimise anyPlayerCloseEnoughForSpawning
+ long lastAutoSaveTime; // Paper - incremental autosave
@ -110,7 +110,7 @@ index 626bcbc6dd013260c3f8b38a1d14e7ba35dc1e01..9e96b0465717bfa761289c255fd8d2f1
public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) {
this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size());
@@ -473,7 +475,19 @@ public class ChunkHolder {
@@ -478,7 +480,19 @@ public class ChunkHolder {
boolean flag2 = playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
boolean flag3 = playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
@ -130,7 +130,7 @@ index 626bcbc6dd013260c3f8b38a1d14e7ba35dc1e01..9e96b0465717bfa761289c255fd8d2f1
if (!flag2 && flag3) {
int expectCreateCount = ++this.fullChunkCreateCount; // Paper
this.fullChunkFuture = chunkStorage.prepareAccessibleChunk(this);
@@ -604,9 +618,33 @@ public class ChunkHolder {
@@ -609,9 +623,33 @@ public class ChunkHolder {
}
public void refreshAccessibility() {
@ -165,7 +165,7 @@ index 626bcbc6dd013260c3f8b38a1d14e7ba35dc1e01..9e96b0465717bfa761289c255fd8d2f1
for (int i = 0; i < this.futures.length(); ++i) {
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = (CompletableFuture) this.futures.get(i);
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index e35a47e0c8eebbad7154a1357a6b868887bc4a0c..cac9c9ede6024653f4ae83cbbdd9939f75ccad48 100644
index 25c71ca93c54976cc319abb0d932beb6f95deb79..e66e4d1656e5f6f042c5c63715e13a6b5e7d81c5 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -101,6 +101,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana
@ -241,7 +241,7 @@ index e35a47e0c8eebbad7154a1357a6b868887bc4a0c..cac9c9ede6024653f4ae83cbbdd9939f
protected void saveAllChunks(boolean flush) {
if (flush) {
List<ChunkHolder> list = (List) this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList());
@@ -751,13 +810,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -752,13 +811,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
int l = 0;
@ -256,7 +256,7 @@ index e35a47e0c8eebbad7154a1357a6b868887bc4a0c..cac9c9ede6024653f4ae83cbbdd9939f
}
@@ -795,6 +848,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -796,6 +849,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.level.unload(chunk);
}
@ -264,7 +264,7 @@ index e35a47e0c8eebbad7154a1357a6b868887bc4a0c..cac9c9ede6024653f4ae83cbbdd9939f
this.lightEngine.updateChunkStatus(ichunkaccess.getPos());
this.lightEngine.tryScheduleUpdate();
@@ -1192,6 +1246,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1193,6 +1247,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
asyncSaveData, chunk);
chunk.setUnsaved(false);
@ -272,7 +272,7 @@ index e35a47e0c8eebbad7154a1357a6b868887bc4a0c..cac9c9ede6024653f4ae83cbbdd9939f
}
// Paper end
@@ -1201,6 +1256,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1202,6 +1257,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (!chunk.isUnsaved()) {
return false;
} else {
@ -281,7 +281,7 @@ index e35a47e0c8eebbad7154a1357a6b868887bc4a0c..cac9c9ede6024653f4ae83cbbdd9939f
ChunkPos chunkcoordintpair = chunk.getPos();
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index b327bd2f48166a4a0c831b0209695fe5935f6a68..73b1018b0aca377ed0ff188e74040cef57036b0b 100644
index 59cd2b8ecdd2c962a027526fe45032559aa2ee8c..fdc027bc5a7e9d691ac90c5fee0cdfebd959bc5f 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -825,6 +825,15 @@ public class ServerChunkCache extends ChunkSource {
@ -301,7 +301,7 @@ index b327bd2f48166a4a0c831b0209695fe5935f6a68..73b1018b0aca377ed0ff188e74040cef
public void close() throws IOException {
// CraftBukkit start
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 0d70647bdd08371beaf476bd4eb5a45d985e498d..b27fb07aacb66259f640de5c5aa6849eb7e8cc9c 100644
index 04aae918f1951291459ce17b25986e5cac9d2378..3b99a6dda3638f3a7212d7c9fab5470409de4e6f 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1058,6 +1058,37 @@ public class ServerLevel extends Level implements WorldGenLevel {

View file

@ -80,7 +80,7 @@ index 89e0181af99cba2368f875fc192342efc972f2ef..b3516862d796c2d9fcc1c67a60734454
chunkData.addProperty("queued-for-unload", chunkMap.toDrop.contains(playerChunk.pos.longKey));
chunkData.addProperty("status", status == null ? "unloaded" : status.toString());
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index 9e96b0465717bfa761289c255fd8d2f1df1be3d8..87271552aa85626f22f7f8569c8fb48fe4b30bf3 100644
index 5479deb3f80a95c58cb6f0f532b2f56b71a68a3a..b62d7a85db012265dd4fae02cbed5363a41d536b 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -57,7 +57,7 @@ public class ChunkHolder {
@ -100,7 +100,7 @@ index 9e96b0465717bfa761289c255fd8d2f1df1be3d8..87271552aa85626f22f7f8569c8fb48f
boolean isUpdateQueued = false; // Paper
private final ChunkMap chunkMap; // Paper
@@ -419,12 +420,18 @@ public class ChunkHolder {
@@ -424,12 +425,18 @@ public class ChunkHolder {
});
}
@ -119,7 +119,7 @@ index 9e96b0465717bfa761289c255fd8d2f1df1be3d8..87271552aa85626f22f7f8569c8fb48f
ChunkStatus chunkstatus = ChunkHolder.getStatus(this.oldTicketLevel);
ChunkStatus chunkstatus1 = ChunkHolder.getStatus(this.ticketLevel);
boolean flag = this.oldTicketLevel <= ChunkMap.MAX_CHUNK_DISTANCE;
@@ -435,9 +442,22 @@ public class ChunkHolder {
@@ -440,9 +447,22 @@ public class ChunkHolder {
// ChunkUnloadEvent: Called before the chunk is unloaded: isChunkLoaded is still true and chunk can still be modified by plugins.
if (playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && !playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
this.getFutureIfPresentUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
@ -143,7 +143,7 @@ index 9e96b0465717bfa761289c255fd8d2f1df1be3d8..87271552aa85626f22f7f8569c8fb48f
// Minecraft will apply the chunks tick lists to the world once the chunk got loaded, and then store the tick
// lists again inside the chunk once the chunk becomes inaccessible and set the chunk's needsSaving flag.
// These actions may however happen deferred, so we manually set the needsSaving flag already here.
@@ -494,12 +514,14 @@ public class ChunkHolder {
@@ -499,12 +519,14 @@ public class ChunkHolder {
this.scheduleFullChunkPromotion(chunkStorage, this.fullChunkFuture, executor, ChunkHolder.FullChunkStatus.BORDER);
// Paper start - cache ticking ready status
this.fullChunkFuture.thenAccept(either -> {
@ -158,7 +158,7 @@ index 9e96b0465717bfa761289c255fd8d2f1df1be3d8..87271552aa85626f22f7f8569c8fb48f
}
});
this.updateChunkToSave(this.fullChunkFuture, "full");
@@ -520,6 +542,7 @@ public class ChunkHolder {
@@ -525,6 +547,7 @@ public class ChunkHolder {
this.scheduleFullChunkPromotion(chunkStorage, this.tickingChunkFuture, executor, ChunkHolder.FullChunkStatus.TICKING);
// Paper start - cache ticking ready status
this.tickingChunkFuture.thenAccept(either -> {
@ -166,7 +166,7 @@ index 9e96b0465717bfa761289c255fd8d2f1df1be3d8..87271552aa85626f22f7f8569c8fb48f
either.ifLeft(chunk -> {
// note: Here is a very good place to add callbacks to logic waiting on this.
ChunkHolder.this.isTickingReady = true;
@@ -555,6 +578,7 @@ public class ChunkHolder {
@@ -560,6 +583,7 @@ public class ChunkHolder {
this.scheduleFullChunkPromotion(chunkStorage, this.entityTickingChunkFuture, executor, ChunkHolder.FullChunkStatus.ENTITY_TICKING);
// Paper start - cache ticking ready status
this.entityTickingChunkFuture.thenAccept(either -> {
@ -174,7 +174,7 @@ index 9e96b0465717bfa761289c255fd8d2f1df1be3d8..87271552aa85626f22f7f8569c8fb48f
either.ifLeft(chunk -> {
ChunkHolder.this.isEntityTickingReady = true;
// Paper start - entity ticking chunk set
@@ -581,16 +605,45 @@ public class ChunkHolder {
@@ -586,16 +610,45 @@ public class ChunkHolder {
this.demoteFullChunk(chunkStorage, playerchunk_state1);
}
@ -223,7 +223,7 @@ index 9e96b0465717bfa761289c255fd8d2f1df1be3d8..87271552aa85626f22f7f8569c8fb48f
});
}
}).exceptionally((throwable) -> {
@@ -705,7 +758,134 @@ public class ChunkHolder {
@@ -710,7 +763,134 @@ public class ChunkHolder {
};
}
@ -360,7 +360,7 @@ index 9e96b0465717bfa761289c255fd8d2f1df1be3d8..87271552aa85626f22f7f8569c8fb48f
return this.isEntityTickingReady;
}
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 92cb20466d2747e6013bf433d87c5bb30a89ba4f..11b3a3105cac879d8a52290967b9d3b638fb3721 100644
index e66e4d1656e5f6f042c5c63715e13a6b5e7d81c5..aef2974ecec878a014ada9619d814ae333f4923a 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -128,6 +128,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@ -448,7 +448,7 @@ index 92cb20466d2747e6013bf433d87c5bb30a89ba4f..11b3a3105cac879d8a52290967b9d3b6
list1.add(playerchunk);
list.add(completablefuture);
@@ -888,11 +938,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -889,11 +939,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (requiredStatus == ChunkStatus.EMPTY) {
return this.scheduleChunkLoad(chunkcoordintpair);
} else {
@ -469,7 +469,7 @@ index 92cb20466d2747e6013bf433d87c5bb30a89ba4f..11b3a3105cac879d8a52290967b9d3b6
if (optional.isPresent() && ((ChunkAccess) optional.get()).getStatus().isOrAfter(requiredStatus)) {
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = requiredStatus.load(this.level, this.structureManager, this.lightEngine, (ichunkaccess) -> {
@@ -904,6 +962,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -905,6 +963,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} else {
return this.scheduleChunkGeneration(holder, requiredStatus);
}
@ -477,7 +477,7 @@ index 92cb20466d2747e6013bf433d87c5bb30a89ba4f..11b3a3105cac879d8a52290967b9d3b6
}
}
@@ -960,14 +1019,24 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -961,14 +1020,24 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
};
CompletableFuture<CompoundTag> chunkSaveFuture = this.level.asyncChunkTaskManager.getChunkSaveFuture(pos.x, pos.z);
@ -507,7 +507,7 @@ index 92cb20466d2747e6013bf433d87c5bb30a89ba4f..11b3a3105cac879d8a52290967b9d3b6
return ret;
// Paper end
}
@@ -1019,7 +1088,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1020,7 +1089,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.releaseLightTicket(chunkcoordintpair);
return CompletableFuture.completedFuture(Either.right(playerchunk_failure));
});
@ -519,7 +519,7 @@ index 92cb20466d2747e6013bf433d87c5bb30a89ba4f..11b3a3105cac879d8a52290967b9d3b6
}
protected void releaseLightTicket(ChunkPos pos) {
@@ -1103,7 +1175,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1104,7 +1176,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
long i = chunkHolder.getPos().toLong();
Objects.requireNonNull(chunkHolder);
@ -529,10 +529,10 @@ index 92cb20466d2747e6013bf433d87c5bb30a89ba4f..11b3a3105cac879d8a52290967b9d3b6
}
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
index cd81e7844c985d7d0f0930faa96478af6a7f6cd4..1744f4983b24a87f3861ebd5d68120cfce904934 100644
index 93a642cd6cf21a268074a73bff22249d32992ad3..7c66e4ed02c80521196b4ca797477dd9573752d8 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
@@ -113,6 +113,7 @@ public abstract class DistanceManager {
@@ -127,6 +127,7 @@ public abstract class DistanceManager {
}
private static int getTicketLevelAt(SortedArraySet<Ticket<?>> tickets) {
@ -540,7 +540,7 @@ index cd81e7844c985d7d0f0930faa96478af6a7f6cd4..1744f4983b24a87f3861ebd5d68120cf
return !tickets.isEmpty() ? ((Ticket) tickets.first()).getTicketLevel() : ChunkMap.MAX_CHUNK_DISTANCE + 1;
}
@@ -127,6 +128,7 @@ public abstract class DistanceManager {
@@ -141,6 +142,7 @@ public abstract class DistanceManager {
public boolean runAllUpdates(ChunkMap chunkStorage) {
//this.f.a(); // Paper - no longer used
this.tickingTicketsTracker.runAllUpdates();
@ -548,7 +548,7 @@ index cd81e7844c985d7d0f0930faa96478af6a7f6cd4..1744f4983b24a87f3861ebd5d68120cf
this.playerTicketManager.runAllUpdates();
int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE);
boolean flag = i != 0;
@@ -137,11 +139,13 @@ public abstract class DistanceManager {
@@ -151,11 +153,13 @@ public abstract class DistanceManager {
// Paper start
if (!this.pendingChunkUpdates.isEmpty()) {
@ -562,7 +562,7 @@ index cd81e7844c985d7d0f0930faa96478af6a7f6cd4..1744f4983b24a87f3861ebd5d68120cf
// Paper end
return true;
} else {
@@ -177,8 +181,10 @@ public abstract class DistanceManager {
@@ -191,8 +195,10 @@ public abstract class DistanceManager {
return flag;
}
}
@ -573,7 +573,7 @@ index cd81e7844c985d7d0f0930faa96478af6a7f6cd4..1744f4983b24a87f3861ebd5d68120cf
SortedArraySet<Ticket<?>> arraysetsorted = this.getTickets(i);
int j = DistanceManager.getTicketLevelAt(arraysetsorted);
Ticket<?> ticket1 = (Ticket) arraysetsorted.addOrGet(ticket);
@@ -192,7 +198,9 @@ public abstract class DistanceManager {
@@ -206,7 +212,9 @@ public abstract class DistanceManager {
}
boolean removeTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
@ -583,7 +583,7 @@ index cd81e7844c985d7d0f0930faa96478af6a7f6cd4..1744f4983b24a87f3861ebd5d68120cf
boolean removed = false; // CraftBukkit
if (arraysetsorted.remove(ticket)) {
@@ -224,7 +232,12 @@ public abstract class DistanceManager {
@@ -238,7 +246,12 @@ public abstract class DistanceManager {
this.tickets.remove(i);
}
@ -597,7 +597,7 @@ index cd81e7844c985d7d0f0930faa96478af6a7f6cd4..1744f4983b24a87f3861ebd5d68120cf
return removed; // CraftBukkit
}
@@ -272,6 +285,112 @@ public abstract class DistanceManager {
@@ -286,6 +299,112 @@ public abstract class DistanceManager {
});
}

View file

@ -36,10 +36,10 @@ scenario / path:
Previously would have hopped to SERVER around 12+ times there extra.
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index 87271552aa85626f22f7f8569c8fb48fe4b30bf3..80aae4303e011dad13ce818136f0383e12ab5c41 100644
index b62d7a85db012265dd4fae02cbed5363a41d536b..9810810467a9052f3362056e9372dd3d0f56b417 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -87,6 +87,13 @@ public class ChunkHolder {
@@ -92,6 +92,13 @@ public class ChunkHolder {
// Paper end - optimise anyPlayerCloseEnoughForSpawning
long lastAutoSaveTime; // Paper - incremental autosave
long inactiveTimeStart; // Paper - incremental autosave
@ -54,7 +54,7 @@ index 87271552aa85626f22f7f8569c8fb48fe4b30bf3..80aae4303e011dad13ce818136f0383e
public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) {
this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size());
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 3e6cc16ff6f6c7993b15bd807257c0554afb6c77..a737fb56fef791f53ed3b0252e5a0369c22bcc7d 100644
index aef2974ecec878a014ada9619d814ae333f4923a..f4a10bd3bbd421999b5ab2d5896b1aec57eb6a82 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -655,7 +655,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@ -66,7 +66,7 @@ index 3e6cc16ff6f6c7993b15bd807257c0554afb6c77..a737fb56fef791f53ed3b0252e5a0369
}
@Nullable
@@ -1059,6 +1059,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1060,6 +1060,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return "chunkGenerate " + requiredStatus.getName();
});
Executor executor = (runnable) -> {

View file

@ -5,10 +5,10 @@ Subject: [PATCH] Don't mark dirty in invalid locations (SPIGOT-6086)
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index 80aae4303e011dad13ce818136f0383e12ab5c41..17a71e2fce455552c0e8af4073c516c21bc3e208 100644
index 9810810467a9052f3362056e9372dd3d0f56b417..c2a040f735db091b5fb6017aa28a95f91ebcd9d3 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -229,6 +229,7 @@ public class ChunkHolder {
@@ -234,6 +234,7 @@ public class ChunkHolder {
}
public void blockChanged(BlockPos pos) {

View file

@ -8,10 +8,10 @@ cause a recursive call which would handle the increase but then
the caller would think the chunk would be unloaded.
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index 17a71e2fce455552c0e8af4073c516c21bc3e208..6bfc705cbf3d8efdb0926df4e1bc6948f9052440 100644
index c2a040f735db091b5fb6017aa28a95f91ebcd9d3..49247d12e743f987113524121e60a3e0ba4a825a 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -438,8 +438,10 @@ public class ChunkHolder {
@@ -443,8 +443,10 @@ public class ChunkHolder {
playerchunkmap.onFullChunkStatusChange(this.pos, playerchunk_state);
}
@ -22,7 +22,7 @@ index 17a71e2fce455552c0e8af4073c516c21bc3e208..6bfc705cbf3d8efdb0926df4e1bc6948
ChunkStatus chunkstatus = ChunkHolder.getStatus(this.oldTicketLevel);
ChunkStatus chunkstatus1 = ChunkHolder.getStatus(this.ticketLevel);
boolean flag = this.oldTicketLevel <= ChunkMap.MAX_CHUNK_DISTANCE;
@@ -481,6 +483,12 @@ public class ChunkHolder {
@@ -486,6 +488,12 @@ public class ChunkHolder {
// Run callback right away if the future was already done
chunkStorage.callbackExecutor.run();

View file

@ -10,10 +10,10 @@ chunk future to complete. We can simply schedule to the immediate
executor to get this effect, rather than the main mailbox.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index ea0e86c3eb124a3ea10276776bfc9f32f4079792..01f95016dc8974db6d6fc09f54294673d83c2297 100644
index 73cd4d12a4991a69adf3f19665bfb5672b7f2ecf..a7bc1d6ce7eebfe0b10f8c179b6a1441169d8fc1 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1272,9 +1272,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1273,9 +1273,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return chunk;
});

View file

@ -8,7 +8,7 @@ Sync loading the chunk at this stage would cause it to load
older data, as well as screwing our region state.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 382532963a856478aa663d9b66e2169eaa36aefc..ba5dab2d3bc8325e5678ef58fedac5b2bc7e38d3 100644
index a7bc1d6ce7eebfe0b10f8c179b6a1441169d8fc1..c773d6ca536389aac5efda6112440e1b0b85594d 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -309,6 +309,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@ -27,7 +27,7 @@ index 382532963a856478aa663d9b66e2169eaa36aefc..ba5dab2d3bc8325e5678ef58fedac5b2
if (k > ChunkMap.MAX_CHUNK_DISTANCE && level > ChunkMap.MAX_CHUNK_DISTANCE) {
return holder;
} else {
@@ -883,6 +885,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -884,6 +886,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (completablefuture1 != completablefuture) {
this.scheduleUnload(pos, holder);
} else {
@ -40,7 +40,7 @@ index 382532963a856478aa663d9b66e2169eaa36aefc..ba5dab2d3bc8325e5678ef58fedac5b2
// Paper start
boolean removed;
if ((removed = this.pendingUnloads.remove(pos, holder)) && ichunkaccess != null) {
@@ -919,6 +927,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -920,6 +928,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z);
}
} // Paper end
@ -49,7 +49,7 @@ index 382532963a856478aa663d9b66e2169eaa36aefc..ba5dab2d3bc8325e5678ef58fedac5b2
}
};
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 55cc46fb065246ef9fc8332ecc1db75a20986c81..b3b10baad56bf14a31a11a3375000195ce84d4d3 100644
index 9cb9bd439be0595e8c94bf128fecc2bd6c1647fc..fc404951b8a910594ffa42aee5ebb8bc3d9bbca4 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -820,6 +820,7 @@ public class ServerChunkCache extends ChunkSource {

View file

@ -8,10 +8,10 @@ This WILL cause state corruption if it happens. So, don't
allow it.
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index 6bfc705cbf3d8efdb0926df4e1bc6948f9052440..1f602d50f3212078490c0092ceefd3b17e0b1532 100644
index 49247d12e743f987113524121e60a3e0ba4a825a..b473a0e3235405dde46d9d6cb300e88d6b1d121b 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -418,7 +418,13 @@ public class ChunkHolder {
@@ -423,7 +423,13 @@ public class ChunkHolder {
CompletableFuture<Void> completablefuture1 = new CompletableFuture();
completablefuture1.thenRunAsync(() -> {
@ -25,7 +25,7 @@ index 6bfc705cbf3d8efdb0926df4e1bc6948f9052440..1f602d50f3212078490c0092ceefd3b1
}, executor);
this.pendingFullStateConfirmation = completablefuture1;
completablefuture.thenAccept((either) -> {
@@ -435,7 +441,12 @@ public class ChunkHolder {
@@ -440,7 +446,12 @@ public class ChunkHolder {
private void demoteFullChunk(ChunkMap playerchunkmap, ChunkHolder.FullChunkStatus playerchunk_state) {
this.pendingFullStateConfirmation.cancel(false);

View file

@ -5,11 +5,95 @@ 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 b473a0e3235405dde46d9d6cb300e88d6b1d121b..43f97571f0b837cbba5c3e889edbc5737a6e7b54 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -83,11 +83,21 @@ public class ChunkHolder {
long key = net.minecraft.server.MCUtil.getCoordinateKey(this.pos);
this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
+ // Paper start - optimise chunk tick iteration
+ if (this.needsBroadcastChanges()) {
+ this.chunkMap.needsChangeBroadcasting.add(this);
+ }
+ // Paper end - optimise chunk tick iteration
}
void onChunkRemove() {
this.playersInMobSpawnRange = null;
this.playersInChunkTickRange = null;
+ // Paper start - optimise chunk tick iteration
+ if (this.needsBroadcastChanges()) {
+ this.chunkMap.needsChangeBroadcasting.remove(this);
+ }
+ // Paper end - optimise chunk tick iteration
}
// Paper end - optimise anyPlayerCloseEnoughForSpawning
long lastAutoSaveTime; // Paper - incremental autosave
@@ -242,7 +252,7 @@ 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();
}
@@ -259,6 +269,7 @@ 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) {
@@ -271,8 +282,19 @@ 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();
int i = 0;
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index c773d6ca536389aac5efda6112440e1b0b85594d..a7fcf3042e6d26739051ed37efbef4a722561c5a 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -155,6 +155,7 @@ 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<>();
// CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
public final CallbackExecutor callbackExecutor = new CallbackExecutor();
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 7c3a1622f161409e92c4ccfa13e8c1fc1d66c947..f7b2fa30eac4da6847d77fd3b2bdd73349b41822 100644
index 59ebce5b9f4db5e48b4b46d68e7325f2d2820bd6..ab1b24820a5ec576f33095ad5688aaecd44910f4 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -995,34 +995,46 @@ public class ServerChunkCache extends ChunkSource {
@@ -50,6 +50,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; // Paper
import java.util.function.Function; // Paper
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper
public class ServerChunkCache extends ChunkSource {
@@ -995,34 +996,42 @@ public class ServerChunkCache extends ChunkSource {
this.lastSpawnState = spawnercreature_d;
gameprofilerfiller.popPush("filteringLoadedChunks");
@ -56,23 +140,19 @@ index 7c3a1622f161409e92c4ccfa13e8c1fc1d66c947..f7b2fa30eac4da6847d77fd3b2bdd733
+ LevelChunk chunk1 = iterator1.next();
+ ChunkHolder holder = chunk1.playerChunk;
+ if (holder != null) {
+ gameprofilerfiller.popPush("broadcast");
+ this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
+ holder.broadcastChanges(chunk1);
+ this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
+ gameprofilerfiller.pop();
+ // Paper - move down
+ // Paper end - optimise chunk tick iteration
ChunkPos chunkcoordintpair = chunk1.getPos();
- if (this.level.isPositionEntityTicking(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning
+ if ((true || this.level.isPositionEntityTicking(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration
+ if (this.level.isPositionEntityTicking(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning
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);
}
@@ -1030,7 +1042,16 @@ public class ServerChunkCache extends ChunkSource {
@@ -1030,7 +1039,16 @@ public class ServerChunkCache extends ChunkSource {
this.level.tickChunk(chunk1, k);
}
}
@ -89,18 +169,35 @@ index 7c3a1622f161409e92c4ccfa13e8c1fc1d66c947..f7b2fa30eac4da6847d77fd3b2bdd733
this.level.timings.chunkTicks.stopTiming(); // Paper
gameprofilerfiller.popPush("customSpawners");
if (flag2) {
@@ -1039,13 +1060,7 @@ public class ServerChunkCache extends ChunkSource {
@@ -1038,15 +1056,24 @@ 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 - no, iterating just ONCE is expensive enough! Don't do it TWICE! Code moved up
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.getFullChunkUnchecked()); // 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();
}
}

View file

@ -108,10 +108,10 @@ index 79e5b8a05828bbc07468d2deeb0f4dad51ca12a5..7be369cf60e45a551fb2d274b9514856
} else {
if (this.haveTime()) {
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 579b2ac31034082456de5ee91e3c6906c7199e32..4e7027d739f7d3c8586804fc053931b2dca1de59 100644
index ab1b24820a5ec576f33095ad5688aaecd44910f4..264105bb8b806d64d1a108bb438a8623c502c990 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -1019,6 +1019,7 @@ public class ServerChunkCache extends ChunkSource {
@@ -1020,6 +1020,7 @@ public class ServerChunkCache extends ChunkSource {
iterator1 = shuffled.iterator();
}
@ -119,7 +119,7 @@ index 579b2ac31034082456de5ee91e3c6906c7199e32..4e7027d739f7d3c8586804fc053931b2
try {
while (iterator1.hasNext()) {
LevelChunk chunk1 = iterator1.next();
@@ -1040,6 +1041,7 @@ public class ServerChunkCache extends ChunkSource {
@@ -1037,6 +1038,7 @@ public class ServerChunkCache extends ChunkSource {
if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) {
this.level.tickChunk(chunk1, k);
@ -128,7 +128,7 @@ index 579b2ac31034082456de5ee91e3c6906c7199e32..4e7027d739f7d3c8586804fc053931b2
}
// Paper start - optimise chunk tick iteration
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 9362b39f4cc321bf0d4fc272bc3fb0463c47d4ce..cb327920cfa8d4eec626af1fe42ec1cc5e8953c7 100644
index 3c2c326c974531eda95757893981a28fb52b4d16..b5e60bd6c023cfc9d48fe8c567feff24b00b3a0e 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -201,7 +201,9 @@ public class ServerLevel extends Level implements WorldGenLevel {

View file

@ -35,7 +35,7 @@ index b3516862d796c2d9fcc1c67a6073445403d73088..b61abf227a04b4565c2525e5f469db30
List<ChunkHolder> allChunks = new ArrayList<>(visibleChunks.values());
List<ServerPlayer> players = world.players;
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index f9f1afb49c8dba14d8d9134e84c73fa2e1d13f02..2e11bedafe383242996aeb545d6612f245070e72 100644
index a7fcf3042e6d26739051ed37efbef4a722561c5a..58dc590cf659cba84d7cb4f7205fdb0bdefded79 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -120,9 +120,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@ -52,7 +52,7 @@ index f9f1afb49c8dba14d8d9134e84c73fa2e1d13f02..2e11bedafe383242996aeb545d6612f2
private final Long2ObjectLinkedOpenHashMap<ChunkHolder> pendingUnloads;
public final LongSet entitiesInLevel;
public final ServerLevel level;
@@ -312,7 +314,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -313,7 +315,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
boolean unloadingPlayerChunk = false; // Paper - do not allow ticket level changes while unloading chunks
public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureManager structureManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
@ -61,7 +61,7 @@ index f9f1afb49c8dba14d8d9134e84c73fa2e1d13f02..2e11bedafe383242996aeb545d6612f2
this.pendingUnloads = new Long2ObjectLinkedOpenHashMap();
this.entitiesInLevel = new LongOpenHashSet();
this.toDrop = new LongOpenHashSet();
@@ -535,12 +537,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -536,12 +538,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@Nullable
public ChunkHolder getUpdatingChunkIfPresent(long pos) {
@ -81,7 +81,7 @@ index f9f1afb49c8dba14d8d9134e84c73fa2e1d13f02..2e11bedafe383242996aeb545d6612f2
}
protected IntSupplier getChunkQueueLevel(long pos) {
@@ -702,7 +709,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -703,7 +710,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end
}
@ -90,7 +90,7 @@ index f9f1afb49c8dba14d8d9134e84c73fa2e1d13f02..2e11bedafe383242996aeb545d6612f2
this.modified = true;
}
@@ -782,7 +789,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -783,7 +790,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
protected void saveAllChunks(boolean flush) {
if (flush) {
@ -99,7 +99,7 @@ index f9f1afb49c8dba14d8d9134e84c73fa2e1d13f02..2e11bedafe383242996aeb545d6612f2
MutableBoolean mutableboolean = new MutableBoolean();
do {
@@ -813,7 +820,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -814,7 +821,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
//this.flushWorker(); // Paper - nuke IOWorker
this.level.asyncChunkTaskManager.flush(); // Paper - flush to preserve behavior compat with pre-async behaviour
} else {
@ -108,7 +108,7 @@ index f9f1afb49c8dba14d8d9134e84c73fa2e1d13f02..2e11bedafe383242996aeb545d6612f2
}
}
@@ -847,7 +854,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -848,7 +855,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
while (longiterator.hasNext()) { // Spigot
long j = longiterator.nextLong();
longiterator.remove(); // Spigot
@ -116,8 +116,8 @@ index f9f1afb49c8dba14d8d9134e84c73fa2e1d13f02..2e11bedafe383242996aeb545d6612f2
+ ChunkHolder playerchunk = this.updatingChunks.queueRemove(j); // Paper - Don't copy
if (playerchunk != null) {
this.pendingUnloads.put(j, playerchunk);
@@ -946,7 +953,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
playerchunk.onChunkRemove(); // Paper
@@ -948,7 +955,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (!this.modified) {
return false;
} else {
@ -131,7 +131,7 @@ index f9f1afb49c8dba14d8d9134e84c73fa2e1d13f02..2e11bedafe383242996aeb545d6612f2
this.modified = false;
return true;
}
@@ -1424,7 +1436,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1426,7 +1438,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.viewDistance = j;
this.distanceManager.updatePlayerTickets(this.viewDistance + 1);
@ -140,7 +140,7 @@ index f9f1afb49c8dba14d8d9134e84c73fa2e1d13f02..2e11bedafe383242996aeb545d6612f2
while (objectiterator.hasNext()) {
ChunkHolder playerchunk = (ChunkHolder) objectiterator.next();
@@ -1467,7 +1479,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1469,7 +1481,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public int size() {
@ -149,7 +149,7 @@ index f9f1afb49c8dba14d8d9134e84c73fa2e1d13f02..2e11bedafe383242996aeb545d6612f2
}
public DistanceManager getDistanceManager() {
@@ -1475,13 +1487,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1477,13 +1489,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
protected Iterable<ChunkHolder> getChunks() {

View file

@ -19,10 +19,10 @@ index eada966d7f108a6081be7a848f5c1dfcb1eed676..a977f7483f37df473096b2234dc1308b
public static final Timing midTickChunkTasks = Timings.ofSafe("Mid Tick Chunk Tasks");
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 05f2fad85da8192d097c2349e630349f6c61754c..631eb52d646309cfbd5b4c7c0621c865967c08e7 100644
index 264105bb8b806d64d1a108bb438a8623c502c990..5bd358983f4b501adb0433e10df320d06816e6e7 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -834,6 +834,7 @@ public class ServerChunkCache extends ChunkSource {
@@ -835,6 +835,7 @@ public class ServerChunkCache extends ChunkSource {
public boolean runDistanceManagerUpdates() {
if (distanceManager.delayDistanceManagerTick) return false; // Paper - Chunk priority
if (this.chunkMap.unloadingPlayerChunk) { net.minecraft.server.MinecraftServer.LOGGER.fatal("Cannot tick distance manager while unloading playerchunks", new Throwable()); throw new IllegalStateException("Cannot tick distance manager while unloading playerchunks"); } // Paper
@ -30,7 +30,7 @@ index 05f2fad85da8192d097c2349e630349f6c61754c..631eb52d646309cfbd5b4c7c0621c865
boolean flag = this.distanceManager.runAllUpdates(this.chunkMap);
boolean flag1 = this.chunkMap.promoteChunkMap();
@@ -843,6 +844,7 @@ public class ServerChunkCache extends ChunkSource {
@@ -844,6 +845,7 @@ public class ServerChunkCache extends ChunkSource {
this.clearCache();
return true;
}

View file

@ -22,13 +22,13 @@ With this change I could get all 200 on at 0ms ping.
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 631eb52d646309cfbd5b4c7c0621c865967c08e7..9a07ccbd12675e501a9aebf89ab85adf6fb658ba 100644
index 5bd358983f4b501adb0433e10df320d06816e6e7..98be0ea366732695f76bc5b1a78e0a36060515bd 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -1066,7 +1066,24 @@ public class ServerChunkCache extends ChunkSource {
// Paper - no, iterating just ONCE is expensive enough! Don't do it TWICE! Code moved up
@@ -1078,7 +1078,24 @@ 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) {

View file

@ -7,7 +7,7 @@ Reference2BooleanOpenHashMap is going to have
better lookups than HashMap.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 7353b4e43503b371973c2de6a61e5ca91d613680..31a1a13887bda174181d926d83273a89b59ec789 100644
index 58dc590cf659cba84d7cb4f7205fdb0bdefded79..72443ab38931a3774641bdc52999e7dcd2586de0 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -108,6 +108,7 @@ import org.apache.logging.log4j.LogManager;
@ -18,7 +18,7 @@ index 7353b4e43503b371973c2de6a61e5ca91d613680..31a1a13887bda174181d926d83273a89
public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider {
@@ -2114,7 +2115,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -2116,7 +2117,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final Entity entity;
private final int range;
SectionPos lastSectionPos;

View file

@ -9,29 +9,42 @@ 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 1f602d50f3212078490c0092ceefd3b17e0b1532..825fdb0336b0388dbbc54c8da99781900612031c 100644
index 43f97571f0b837cbba5c3e889edbc5737a6e7b54..f5b6d0f69e727b8f5e425340f8c1381a82a24ce3 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -83,6 +83,12 @@ public class ChunkHolder {
long key = net.minecraft.server.MCUtil.getCoordinateKey(this.pos);
this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
@@ -88,6 +88,12 @@ public class ChunkHolder {
this.chunkMap.needsChangeBroadcasting.add(this);
}
// Paper end - optimise chunk tick iteration
+ // Paper start - optimise checkDespawn
+ LevelChunk chunk = this.getFullChunkUnchecked();
+ if (chunk != null) {
+ chunk.updateGeneralAreaCache();
+ }
+ // Paper end - optimise checkDespawn
}
void onChunkRemove() {
@@ -98,6 +104,12 @@ public class ChunkHolder {
this.chunkMap.needsChangeBroadcasting.remove(this);
}
// Paper end - optimise chunk tick iteration
+ // Paper start - optimise checkDespawn
+ LevelChunk chunk = this.getFullChunkUnchecked();
+ if (chunk != null) {
+ chunk.removeGeneralAreaCache();
+ }
+ // Paper end - optimise checkDespawn
}
// Paper end - optimise anyPlayerCloseEnoughForSpawning
long lastAutoSaveTime; // Paper - incremental autosave
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index dd23e7110259574859a074c423312c452d533668..c56ccb8a6faba0be36b53038e723da5a4461ded1 100644
index 72443ab38931a3774641bdc52999e7dcd2586de0..69533a1239f12c41a255bf9deeb5640695edfa13 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -159,6 +159,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
int viewDistance;
@@ -160,6 +160,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper
public final ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new ReferenceOpenHashSet<>();
+ // Paper start - optimise checkDespawn
+ public static final int GENERAL_AREA_MAP_SQUARE_RADIUS = 40;
@ -43,7 +56,7 @@ index dd23e7110259574859a074c423312c452d533668..c56ccb8a6faba0be36b53038e723da5a
// CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
public final CallbackExecutor callbackExecutor = new CallbackExecutor();
public static final class CallbackExecutor implements java.util.concurrent.Executor, Runnable {
@@ -236,6 +243,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -237,6 +244,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end - use distance map to optimise entity tracker
// Note: players need to be explicitly added to distance maps before they can be updated
this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
@ -51,7 +64,7 @@ index dd23e7110259574859a074c423312c452d533668..c56ccb8a6faba0be36b53038e723da5a
// Paper start - per player mob spawning
if (this.playerMobDistanceMap != null) {
this.playerMobDistanceMap.add(player, chunkX, chunkZ, this.distanceManager.getSimulationDistance());
@@ -254,6 +262,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -255,6 +263,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.playerMobSpawnMap.remove(player);
this.playerChunkTickRangeMap.remove(player);
// Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
@ -59,7 +72,7 @@ index dd23e7110259574859a074c423312c452d533668..c56ccb8a6faba0be36b53038e723da5a
// Paper start - per player mob spawning
if (this.playerMobDistanceMap != null) {
this.playerMobDistanceMap.remove(player);
@@ -274,6 +283,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -275,6 +284,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
// Paper end - use distance map to optimise entity tracker
this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
@ -67,7 +80,7 @@ index dd23e7110259574859a074c423312c452d533668..c56ccb8a6faba0be36b53038e723da5a
// Paper start - per player mob spawning
if (this.playerMobDistanceMap != null) {
this.playerMobDistanceMap.update(player, chunkX, chunkZ, this.distanceManager.getSimulationDistance());
@@ -434,6 +444,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -435,6 +445,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
});
// Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
@ -92,7 +105,7 @@ index dd23e7110259574859a074c423312c452d533668..c56ccb8a6faba0be36b53038e723da5a
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 fe2e5eb72307f8f07987b12a1af9100348b86a82..94798e2715a16b43f11493b08fe342aeecf11cef 100644
index 4b18dfbf559214dc511003bb3396acbcfb80fdda..9c73cb316522302bf88b671398ba53c0eb2d29d5 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -399,6 +399,83 @@ public class ServerLevel extends Level implements WorldGenLevel {
@ -309,10 +322,10 @@ index 6dba117b4f5dc6c4e078a32037a4026b45bf2176..515e58e3db223fbdc01ca87607aca234
private static Boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) { // Paper
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
index 8e03e63a00dd242791ba0d5a8a17922227b16165..4a9a1fef5603b073e6d2d12e3e8e5dca73a7bd1b 100644
index 8e03e63a00dd242791ba0d5a8a17922227b16165..0746300e8c0a29bedb9ec02803c194c2d03b78fb 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -235,6 +235,93 @@ public class LevelChunk extends ChunkAccess {
@@ -235,6 +235,98 @@ public class LevelChunk extends ChunkAccess {
}
}
// Paper end
@ -331,6 +344,11 @@ index 8e03e63a00dd242791ba0d5a8a17922227b16165..4a9a1fef5603b073e6d2d12e3e8e5dca
+ 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;

View file

@ -8,10 +8,10 @@ Instead, only iterate over navigators in the current region that are
eligible for repathing.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 60ae567fe76a643f6a363644bfc99eac2ef64835..634f8690ccce7cdcc524258a6f2844c60b9563b5 100644
index 69533a1239f12c41a255bf9deeb5640695edfa13..3c90c6514dc856490da7fca5c8a42023493836d7 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -296,15 +296,81 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -297,15 +297,81 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public final io.papermc.paper.chunk.SingleThreadChunkRegionManager dataRegionManager;
public static final class DataRegionData implements io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionData {
@ -93,7 +93,7 @@ index 60ae567fe76a643f6a363644bfc99eac2ef64835..634f8690ccce7cdcc524258a6f2844c6
}
@Override
@@ -314,6 +380,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -315,6 +381,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final DataRegionSectionData sectionData = (DataRegionSectionData)section.sectionData;
final DataRegionData oldRegionData = oldRegion == null ? null : (DataRegionData)oldRegion.regionData;
final DataRegionData newRegionData = (DataRegionData)newRegion.regionData;
@ -110,7 +110,7 @@ index 60ae567fe76a643f6a363644bfc99eac2ef64835..634f8690ccce7cdcc524258a6f2844c6
}
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 0918bb28fd058e6b79f45993a46738a50b05b60a..f17c0f501c89c07651a40673ad5ecfe6c7168fce 100644
index 9c73cb316522302bf88b671398ba53c0eb2d29d5..9f1f55d457b1a99c12f7fc94aaf9005fb8df50e7 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1093,6 +1093,7 @@ public class ServerLevel extends Level implements WorldGenLevel {

View file

@ -12,10 +12,10 @@ time to save, as flush saving performs a full flush at
the end anyways.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 634f8690ccce7cdcc524258a6f2844c60b9563b5..b7a215c94e1ef021360dde5c4099ddd5b1f676b8 100644
index 3c90c6514dc856490da7fca5c8a42023493836d7..8678640da7724283664e3f115230d6b2687967b1 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -891,6 +891,16 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -892,6 +892,16 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end
protected void saveAllChunks(boolean flush) {
@ -32,7 +32,7 @@ index 634f8690ccce7cdcc524258a6f2844c60b9563b5..b7a215c94e1ef021360dde5c4099ddd5
if (flush) {
List<ChunkHolder> list = (List) this.updatingChunks.getVisibleValuesCopy().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); // Paper
MutableBoolean mutableboolean = new MutableBoolean();
@@ -913,6 +923,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -914,6 +924,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}).filter((ichunkaccess) -> {
return ichunkaccess instanceof ImposterProtoChunk || ichunkaccess instanceof LevelChunk;
}).filter(this::save).forEach((ichunkaccess) -> {

View file

@ -4345,7 +4345,7 @@ index 0000000000000000000000000000000000000000..dd995e25ae620ae36cd5eecb2fe10ad0
+
+}
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
index 315bd2408e4a45993c9b2572e0ab5260a70522ec..c0d123bff1825366c30aadd3ad8a7fde68ef74e4 100644
index c57999061a7a9adb7b5207a13af3d693529a43cd..76d27205b2bd08ba2b3530b46f0d9b4e2315d82e 100644
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
@@ -700,6 +700,46 @@ public class PaperCommand extends Command {
@ -4419,7 +4419,7 @@ index 315bd2408e4a45993c9b2572e0ab5260a70522ec..c0d123bff1825366c30aadd3ad8a7fde
Deque<ChunkPos> queue = new ArrayDeque<>(MCUtil.getSpiralOutChunks(center, radius));
updateLight(sender, world, lightengine, queue);
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index 825fdb0336b0388dbbc54c8da99781900612031c..d271871563fa883efb77b35ec3b1dfbba87f0b62 100644
index f5b6d0f69e727b8f5e425340f8c1381a82a24ce3..9c4e4ab16441555d7940863b6736a03ee4af545c 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -52,7 +52,7 @@ public class ChunkHolder {
@ -4432,7 +4432,7 @@ index 825fdb0336b0388dbbc54c8da99781900612031c..d271871563fa883efb77b35ec3b1dfbb
private final DebugBuffer<ChunkHolder.ChunkSaveDebug> chunkToSaveHistory;
public int oldTicketLevel;
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index b7a215c94e1ef021360dde5c4099ddd5b1f676b8..02bcffcdde33088e43a7771924b2d2bf47badd09 100644
index 8678640da7724283664e3f115230d6b2687967b1..29fdc1b991c67006a13231abbbe50e201744b5c2 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -130,7 +130,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@ -4961,7 +4961,7 @@ index e15263a152c88371ebc65b47f0be938f7c19a8f2..59c053deb52c9307f1b4c1515384a7c6
super(wrapped.getPos(), UpgradeData.EMPTY, wrapped.levelHeightAccessor, wrapped.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), wrapped.getBlendingData());
this.wrapped = wrapped;
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 4a9a1fef5603b073e6d2d12e3e8e5dca73a7bd1b..7567a8bf848c82b27383f084056cb43c41df6d0c 100644
index 0746300e8c0a29bedb9ec02803c194c2d03b78fb..55c0e9655ded14e25b0f284ad0c1f99eb5d0b192 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -100,6 +100,10 @@ public class LevelChunk extends ChunkAccess {
@ -4975,7 +4975,7 @@ index 4a9a1fef5603b073e6d2d12e3e8e5dca73a7bd1b..7567a8bf848c82b27383f084056cb43c
this.tickersInLevel = Maps.newHashMap();
this.clientLightReady = false;
this.level = (ServerLevel) world; // CraftBukkit - type
@@ -325,6 +329,12 @@ public class LevelChunk extends ChunkAccess {
@@ -330,6 +334,12 @@ public class LevelChunk extends ChunkAccess {
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());
@ -5002,7 +5002,7 @@ index d850cae1ec024a557e62cd561fbca137dc2be96c..eef1b58cfaf3cfa90f3786785dd94d05
return data.palette.valueFor(data.storage.get(index));
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
index 0dfa51c8826b9e984586a3e4e050a50a4fbb1bd3..e947a47dd8c6906bc36eca757c4b9f9f2ab3cedc 100644
index acfd46c7035b4009d61bda8a7c8dd6953e4836e6..9e293afe94e85ecbc2a236b1b34df1a4926b83cb 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
@@ -54,6 +54,12 @@ public class ProtoChunk extends ChunkAccess {

View file

@ -70,10 +70,10 @@ index be668387f65a633c6ac497fca632a4767a1bf3a2..e08f4e39db4ee3fed62e37364d17dcc5
}
diff --git a/src/main/java/io/papermc/paper/util/CollisionUtil.java b/src/main/java/io/papermc/paper/util/CollisionUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..58629451977c89db2fa895bde946135784a0d8bc
index 0000000000000000000000000000000000000000..b25018ab6cff59e43652c953421f01b6d0bb0a85
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/CollisionUtil.java
@@ -0,0 +1,639 @@
@@ -0,0 +1,899 @@
+package io.papermc.paper.util;
+
+import io.papermc.paper.voxel.AABBVoxelShape;
@ -91,6 +91,7 @@ index 0000000000000000000000000000000000000000..58629451977c89db2fa895bde9461357
+import net.minecraft.world.level.border.WorldBorder;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.LevelChunkSection;
+import net.minecraft.world.level.chunk.PalettedContainer;
+import net.minecraft.world.level.material.FlowingFluid;
+import net.minecraft.world.level.material.FluidState;
+import net.minecraft.world.phys.AABB;
@ -101,7 +102,6 @@ index 0000000000000000000000000000000000000000..58629451977c89db2fa895bde9461357
+import net.minecraft.world.phys.shapes.Shapes;
+import net.minecraft.world.phys.shapes.VoxelShape;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BiPredicate;
+import java.util.function.Predicate;
+
@ -109,6 +109,15 @@ index 0000000000000000000000000000000000000000..58629451977c89db2fa895bde9461357
+
+ public static final double COLLISION_EPSILON = 1.0E-7;
+
+ public static final long KNOWN_EMPTY_BLOCK = 0b00; // known to always have voxelshape of empty
+ public static final long KNOWN_FULL_BLOCK = 0b01; // known to always have voxelshape of full cube
+ public static final long KNOWN_UNKNOWN_BLOCK = 0b10; // must read the actual block state for info
+ public static final long KNOWN_SPECIAL_BLOCK = 0b11; // caller must check this block for special collisions
+
+ public static boolean isSpecialCollidingBlock(final net.minecraft.world.level.block.state.BlockBehaviour.BlockStateBase block) {
+ return block.shapeExceedsCube() || block.getBlock() == Blocks.MOVING_PISTON;
+ }
+
+ public static boolean isEmpty(final AABB aabb) {
+ return (aabb.maxX - aabb.minX) < COLLISION_EPSILON && (aabb.maxY - aabb.minY) < COLLISION_EPSILON && (aabb.maxZ - aabb.minZ) < COLLISION_EPSILON;
+ }
@ -471,8 +480,8 @@ index 0000000000000000000000000000000000000000..58629451977c89db2fa895bde9461357
+ }
+
+ public static boolean getCollisionsForBlocksOrWorldBorder(final CollisionGetter getter, final Entity entity, final AABB aabb,
+ final List<AABB> into, final boolean loadChunks, final boolean collidesWithUnloaded,
+ final boolean checkBorder, final boolean checkOnly, final BiPredicate<BlockState, BlockPos> predicate) {
+ final List<AABB> into, final boolean loadChunks, final boolean collidesWithUnloaded,
+ final boolean checkBorder, final boolean checkOnly, final BiPredicate<BlockState, BlockPos> predicate) {
+ boolean ret = false;
+
+ if (checkBorder) {
@ -486,21 +495,21 @@ index 0000000000000000000000000000000000000000..58629451977c89db2fa895bde9461357
+ }
+ }
+
+ int minBlockX = Mth.floor(aabb.minX - COLLISION_EPSILON) - 1;
+ int maxBlockX = Mth.floor(aabb.maxX + COLLISION_EPSILON) + 1;
+ final int minBlockX = Mth.floor(aabb.minX - COLLISION_EPSILON) - 1;
+ final int maxBlockX = Mth.floor(aabb.maxX + COLLISION_EPSILON) + 1;
+
+ int minBlockY = Mth.floor(aabb.minY - COLLISION_EPSILON) - 1;
+ int maxBlockY = Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1;
+ final int minBlockY = Mth.floor(aabb.minY - COLLISION_EPSILON) - 1;
+ final int maxBlockY = Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1;
+
+ int minBlockZ = Mth.floor(aabb.minZ - COLLISION_EPSILON) - 1;
+ int maxBlockZ = Mth.floor(aabb.maxZ + COLLISION_EPSILON) + 1;
+ final int minBlockZ = Mth.floor(aabb.minZ - COLLISION_EPSILON) - 1;
+ final int maxBlockZ = Mth.floor(aabb.maxZ + COLLISION_EPSILON) + 1;
+
+ final int minSection = WorldUtil.getMinSection(getter);
+ final int maxSection = WorldUtil.getMaxSection(getter);
+ final int minBlock = minSection << 4;
+ final int maxBlock = (maxSection << 4) | 15;
+
+ BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
+ final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
+ CollisionContext collisionShape = null;
+
+ // special cases:
@ -509,16 +518,22 @@ index 0000000000000000000000000000000000000000..58629451977c89db2fa895bde9461357
+ return ret;
+ }
+
+ int minYIterate = Math.max(minBlock, minBlockY);
+ int maxYIterate = Math.min(maxBlock, maxBlockY);
+ final int minYIterate = Math.max(minBlock, minBlockY);
+ final int maxYIterate = Math.min(maxBlock, maxBlockY);
+
+ int minChunkX = minBlockX >> 4;
+ int maxChunkX = maxBlockX >> 4;
+ final int minChunkX = minBlockX >> 4;
+ final int maxChunkX = maxBlockX >> 4;
+
+ int minChunkZ = minBlockZ >> 4;
+ int maxChunkZ = maxBlockZ >> 4;
+ final int minChunkY = minBlockY >> 4;
+ final int maxChunkY = maxBlockY >> 4;
+
+ ServerChunkCache chunkProvider;
+ final int minChunkYIterate = minYIterate >> 4;
+ final int maxChunkYIterate = maxYIterate >> 4;
+
+ final int minChunkZ = minBlockZ >> 4;
+ final int maxChunkZ = maxBlockZ >> 4;
+
+ final ServerChunkCache chunkProvider;
+ if (getter instanceof WorldGenRegion) {
+ chunkProvider = null;
+ } else if (getter instanceof ServerLevel) {
@ -526,26 +541,24 @@ index 0000000000000000000000000000000000000000..58629451977c89db2fa895bde9461357
+ } else {
+ chunkProvider = null;
+ }
+ // TODO special case single chunk?
+
+ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
+ int minZ = currChunkZ == minChunkZ ? minBlockZ & 15 : 0; // coordinate in chunk
+ int maxZ = currChunkZ == maxChunkZ ? maxBlockZ & 15 : 15; // coordinate in chunk
+ final int minZ = currChunkZ == minChunkZ ? minBlockZ & 15 : 0; // coordinate in chunk
+ final int maxZ = currChunkZ == maxChunkZ ? maxBlockZ & 15 : 15; // coordinate in chunk
+
+ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
+ int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk
+ int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 15; // coordinate in chunk
+ final int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk
+ final int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 15; // coordinate in chunk
+
+ int chunkXGlobalPos = currChunkX << 4;
+ int chunkZGlobalPos = currChunkZ << 4;
+ ChunkAccess chunk;
+ final int chunkXGlobalPos = currChunkX << 4;
+ final int chunkZGlobalPos = currChunkZ << 4;
+ final ChunkAccess chunk;
+ if (chunkProvider == null) {
+ chunk = (ChunkAccess)getter.getChunkForCollisions(currChunkX, currChunkZ);
+ } else {
+ chunk = loadChunks ? chunkProvider.getChunk(currChunkX, currChunkZ, true) : chunkProvider.getChunkAtIfLoadedImmediately(currChunkX, currChunkZ);
+ }
+
+
+ if (chunk == null) {
+ if (collidesWithUnloaded) {
+ if (checkOnly) {
@ -558,59 +571,306 @@ index 0000000000000000000000000000000000000000..58629451977c89db2fa895bde9461357
+ continue;
+ }
+
+ LevelChunkSection[] sections = chunk.getSections();
+ final LevelChunkSection[] sections = chunk.getSections();
+
+ // bound y
+
+ for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
+ LevelChunkSection section = sections[(currY >> 4) - minSection];
+ if (section.hasOnlyAir()) {
+ for (int currChunkY = minChunkYIterate; currChunkY <= maxChunkYIterate; ++currChunkY) {
+ final LevelChunkSection section = sections[currChunkY - minSection];
+ if (section == null || section.hasOnlyAir()) {
+ // empty
+ // skip to next section
+ currY = (currY & ~(15)) + 15; // increment by 15: iterator loop increments by the extra one
+ continue;
+ }
+ final PalettedContainer<BlockState> blocks = section.states;
+
+ net.minecraft.world.level.chunk.PalettedContainer<BlockState> blocks = section.states;
+ final int minY = currChunkY == minChunkYIterate ? minYIterate & 15 : 0; // coordinate in chunk
+ final int maxY = currChunkY == maxChunkYIterate ? maxYIterate & 15 : 15; // coordinate in chunk
+ final int chunkYGlobalPos = currChunkY << 4;
+
+ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
+ for (int currX = minX; currX <= maxX; ++currX) {
+ int localBlockIndex = (currX) | (currZ << 4) | ((currY & 15) << 8);
+ int blockX = currX | chunkXGlobalPos;
+ int blockY = currY;
+ int blockZ = currZ | chunkZGlobalPos;
+ final boolean sectionHasSpecial = section.hasSpecialCollidingBlocks();
+
+ int edgeCount = ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) +
+ ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) +
+ ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0);
+ if (edgeCount == 3) {
+ final int minXIterate;
+ final int maxXIterate;
+ final int minZIterate;
+ final int maxZIterate;
+ final int minYIterateLocal;
+ final int maxYIterateLocal;
+
+ if (!sectionHasSpecial) {
+ minXIterate = currChunkX == minChunkX ? minX + 1 : minX;
+ maxXIterate = currChunkX == maxChunkX ? maxX - 1 : maxX;
+ minZIterate = currChunkZ == minChunkZ ? minZ + 1 : minZ;
+ maxZIterate = currChunkZ == maxChunkZ ? maxZ - 1 : maxZ;
+ minYIterateLocal = currChunkY == minChunkY ? minY + 1 : minY;
+ maxYIterateLocal = currChunkY == maxChunkY ? maxY - 1 : maxY;
+ if (minXIterate > maxXIterate || minZIterate > maxZIterate) {
+ continue;
+ }
+ } else {
+ minXIterate = minX;
+ maxXIterate = maxX;
+ minZIterate = minZ;
+ maxZIterate = maxZ;
+ minYIterateLocal = minY;
+ maxYIterateLocal = maxY;
+ }
+
+ for (int currY = minYIterateLocal; currY <= maxYIterateLocal; ++currY) {
+ long collisionForHorizontal = section.getKnownBlockInfoHorizontalRaw(currY, minZIterate & 15);
+ for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ,
+ collisionForHorizontal = (currZ & 1) == 0 ? section.getKnownBlockInfoHorizontalRaw(currY, currZ & 15) : collisionForHorizontal) {
+ // From getKnownBlockInfoHorizontalRaw:
+ // important detail: this returns 32 values, one for localZ = localZ & (~1) and one for localZ = localZ | 1
+ // the even localZ is the lower 32 bits, the odd is the upper 32 bits
+ // We want to use a bitset to only iterate over non-empty blocks.
+ // We need to build a bitset mask to and out the other collisions we just don't care at all about
+ // First, we need to build a bitset from 0..n*2 where n is the number of blocks on the x axis
+ // It's important to note that the iterate values can be outside [0, 15], but if they are,
+ // then none of the x or z loops would meet their conditions. So we can assume they are never
+ // out of bounds here
+ final int xAxisBits = (maxXIterate - minXIterate + 1) << 1; // << 1 -> * 2 // Never > 32
+ long bitset = (1L << xAxisBits) - 1;
+ // Now we need to offset it by 32 bits if current Z is odd (lower 32 bits is 16 block infos for even z, upper is for odd)
+ int shift = (currZ & 1) << 5; // this will be a LEFT shift
+ // Now we need to offset shift so that the bitset first position is at minXIterate
+ shift += (minXIterate << 1); // 0th pos -> 0th bit, 1st pos -> 2nd bit, ...
+
+ // all done
+ bitset = bitset << shift;
+ if ((collisionForHorizontal & bitset) == 0L) {
+ // All empty
+ continue;
+ }
+ for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
+ final int localBlockIndex = (currX) | (currZ << 4) | (currY << 8);
+
+ BlockState blockData = blocks.get(localBlockIndex);
+ if (blockData.isAir()) {
+ continue;
+ }
+ final int blockInfo = (int) LevelChunkSection.getKnownBlockInfo(localBlockIndex, collisionForHorizontal);
+
+ if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) {
+ mutablePos.set(blockX, blockY, blockZ);
+ if (collisionShape == null) {
+ collisionShape = new LazyEntityCollisionContext(entity);
+ }
+ VoxelShape voxelshape2 = blockData.getCollisionShape(getter, mutablePos, collisionShape);
+ if (voxelshape2 != Shapes.empty()) {
+ VoxelShape voxelshape3 = voxelshape2.move((double)blockX, (double)blockY, (double)blockZ);
+
+ if (predicate != null && !predicate.test(blockData, mutablePos)) {
+ switch (blockInfo) {
+ case (int) CollisionUtil.KNOWN_EMPTY_BLOCK: {
+ continue;
+ }
+
+ if (checkOnly) {
+ if (voxelshape3.intersects(aabb)) {
+ return true;
+ case (int) CollisionUtil.KNOWN_FULL_BLOCK: {
+ double blockX = (double)(currX | chunkXGlobalPos);
+ double blockY = (double)(currY | chunkYGlobalPos);
+ double blockZ = (double)(currZ | chunkZGlobalPos);
+ final AABB blockBox = new AABB(
+ blockX, blockY, blockZ,
+ blockX + 1.0, blockY + 1.0, blockZ + 1.0,
+ true
+ );
+ if (predicate != null) {
+ if (!voxelShapeIntersect(aabb, blockBox)) {
+ continue;
+ }
+ // fall through to get the block for the predicate
+ } else {
+ if (voxelShapeIntersect(aabb, blockBox)) {
+ if (checkOnly) {
+ return true;
+ } else {
+ into.add(blockBox);
+ ret = true;
+ }
+ }
+ continue;
+ }
+ }
+ // default: fall through to standard logic
+ }
+
+ int blockX = currX | chunkXGlobalPos;
+ int blockY = currY | chunkYGlobalPos;
+ int blockZ = currZ | chunkZGlobalPos;
+
+ int edgeCount = ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) +
+ ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) +
+ ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0);
+ if (edgeCount == 3) {
+ continue;
+ }
+
+ BlockState blockData = blocks.get(localBlockIndex);
+
+ if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) {
+ mutablePos.set(blockX, blockY, blockZ);
+ if (collisionShape == null) {
+ collisionShape = new LazyEntityCollisionContext(entity);
+ }
+ VoxelShape voxelshape2 = blockData.getCollisionShape(getter, mutablePos, collisionShape);
+ if (voxelshape2 != Shapes.empty()) {
+ VoxelShape voxelshape3 = voxelshape2.move((double)blockX, (double)blockY, (double)blockZ);
+
+ if (predicate != null && !predicate.test(blockData, mutablePos)) {
+ continue;
+ }
+
+ if (checkOnly) {
+ if (voxelshape3.intersects(aabb)) {
+ return true;
+ }
+ } else {
+ ret |= addBoxesToIfIntersects(voxelshape3, aabb, into);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ public static boolean getCollisionsForBlocksOrWorldBorderReference(final CollisionGetter getter, final Entity entity, final AABB aabb,
+ final List<AABB> into, final boolean loadChunks, final boolean collidesWithUnloaded,
+ final boolean checkBorder, final boolean checkOnly, final BiPredicate<BlockState, BlockPos> predicate) {
+ boolean ret = false;
+
+ if (checkBorder) {
+ if (CollisionUtil.isAlmostCollidingOnBorder(getter.getWorldBorder(), aabb)) {
+ if (checkOnly) {
+ return true;
+ } else {
+ CollisionUtil.addBoxesTo(getter.getWorldBorder().getCollisionShape(), into);
+ ret = true;
+ }
+ }
+ }
+
+ final int minBlockX = Mth.floor(aabb.minX - COLLISION_EPSILON) - 1;
+ final int maxBlockX = Mth.floor(aabb.maxX + COLLISION_EPSILON) + 1;
+
+ final int minBlockY = Mth.floor(aabb.minY - COLLISION_EPSILON) - 1;
+ final int maxBlockY = Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1;
+
+ final int minBlockZ = Mth.floor(aabb.minZ - COLLISION_EPSILON) - 1;
+ final int maxBlockZ = Mth.floor(aabb.maxZ + COLLISION_EPSILON) + 1;
+
+ final int minSection = WorldUtil.getMinSection(getter);
+ final int maxSection = WorldUtil.getMaxSection(getter);
+ final int minBlock = minSection << 4;
+ final int maxBlock = (maxSection << 4) | 15;
+
+ final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
+ CollisionContext collisionShape = null;
+
+ // special cases:
+ if (minBlockY > maxBlock || maxBlockY < minBlock) {
+ // no point in checking
+ return ret;
+ }
+
+ final int minYIterate = Math.max(minBlock, minBlockY);
+ final int maxYIterate = Math.min(maxBlock, maxBlockY);
+
+ final int minChunkX = minBlockX >> 4;
+ final int maxChunkX = maxBlockX >> 4;
+
+ final int minChunkY = minBlockY >> 4;
+ final int maxChunkY = maxBlockY >> 4;
+
+ final int minChunkYIterate = minYIterate >> 4;
+ final int maxChunkYIterate = maxYIterate >> 4;
+
+ final int minChunkZ = minBlockZ >> 4;
+ final int maxChunkZ = maxBlockZ >> 4;
+
+ final ServerChunkCache chunkProvider;
+ if (getter instanceof WorldGenRegion) {
+ chunkProvider = null;
+ } else if (getter instanceof ServerLevel) {
+ chunkProvider = ((ServerLevel)getter).getChunkSource();
+ } else {
+ chunkProvider = null;
+ }
+
+ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
+ final int minZ = currChunkZ == minChunkZ ? minBlockZ & 15 : 0; // coordinate in chunk
+ final int maxZ = currChunkZ == maxChunkZ ? maxBlockZ & 15 : 15; // coordinate in chunk
+
+ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
+ final int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk
+ final int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 15; // coordinate in chunk
+
+ final int chunkXGlobalPos = currChunkX << 4;
+ final int chunkZGlobalPos = currChunkZ << 4;
+ final ChunkAccess chunk;
+ if (chunkProvider == null) {
+ chunk = (ChunkAccess)getter.getChunkForCollisions(currChunkX, currChunkZ);
+ } else {
+ chunk = loadChunks ? chunkProvider.getChunk(currChunkX, currChunkZ, true) : chunkProvider.getChunkAtIfLoadedImmediately(currChunkX, currChunkZ);
+ }
+
+ if (chunk == null) {
+ if (collidesWithUnloaded) {
+ if (checkOnly) {
+ return true;
+ } else {
+ into.add(getBoxForChunk(currChunkX, currChunkZ));
+ ret = true;
+ }
+ }
+ continue;
+ }
+
+ final LevelChunkSection[] sections = chunk.getSections();
+
+ // bound y
+ for (int currChunkY = minChunkYIterate; currChunkY <= maxChunkYIterate; ++currChunkY) {
+ final LevelChunkSection section = sections[currChunkY - minSection];
+ if (section == null || section.hasOnlyAir()) {
+ // empty
+ continue;
+ }
+ final PalettedContainer<BlockState> blocks = section.states;
+
+ final int minY = currChunkY == minChunkYIterate ? minYIterate & 15 : 0; // coordinate in chunk
+ final int maxY = currChunkY == maxChunkYIterate ? maxYIterate & 15 : 15; // coordinate in chunk
+ final int chunkYGlobalPos = currChunkY << 4;
+
+ for (int currY = minY; currY <= maxY; ++currY) {
+ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
+ for (int currX = minX; currX <= maxX; ++currX) {
+ int localBlockIndex = (currX) | (currZ << 4) | ((currY) << 8);
+ int blockX = currX | chunkXGlobalPos;
+ int blockY = currY | chunkYGlobalPos;
+ int blockZ = currZ | chunkZGlobalPos;
+
+ int edgeCount = ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) +
+ ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) +
+ ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0);
+ if (edgeCount == 3) {
+ continue;
+ }
+
+ BlockState blockData = blocks.get(localBlockIndex);
+ if (blockData.getBlockCollisionBehavior() == CollisionUtil.KNOWN_EMPTY_BLOCK) {
+ continue;
+ }
+
+ if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) {
+ mutablePos.set(blockX, blockY, blockZ);
+ if (collisionShape == null) {
+ collisionShape = new LazyEntityCollisionContext(entity);
+ }
+ VoxelShape voxelshape2 = blockData.getCollisionShape(getter, mutablePos, collisionShape);
+ if (voxelshape2 != Shapes.empty()) {
+ VoxelShape voxelshape3 = voxelshape2.move((double)blockX, (double)blockY, (double)blockZ);
+
+ if (predicate != null && !predicate.test(blockData, mutablePos)) {
+ continue;
+ }
+
+ if (checkOnly) {
+ if (voxelshape3.intersects(aabb)) {
+ return true;
+ }
+ } else {
+ ret |= addBoxesToIfIntersects(voxelshape3, aabb, into);
+ }
+ } else {
+ ret |= addBoxesToIfIntersects(voxelshape3, aabb, into);
+ }
+ }
+ }
@ -955,7 +1215,7 @@ index 80888d5adf7d4377e17e6f530f35053cfcc9eed4..fe9810c3b908339ca050ed832c2e67d6
}
// CraftBukkit start
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 26546c030710685cdc1c75b8018462cd424b9ed6..2e61243b86ea1f21ec799a63badeae329e1ac3fa 100644
index bf3bf6f359bfbef0fa2c6f872ec3ff35739a15fc..fe6f2ec41c521c8f1ac17aa3af8bb7f7375e5a02 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -1082,9 +1082,44 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
@ -1216,18 +1476,202 @@ index f9527d1d867f93b4e0e2758485cfa1f6efa0bf8b..1f4b72a0aca200b2e0860449c718e6e6
return List.of();
} else {
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
index 1831588b275f11aff37573fead835f6ddabfece1..05c46f3b3bce5225b819d86e6e06729a5093e092 100644
index 1831588b275f11aff37573fead835f6ddabfece1..eac017fc521bfd1391e75db8628f42b28329d681 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -726,7 +726,7 @@ public abstract class BlockBehaviour {
@@ -717,6 +717,13 @@ public abstract class BlockBehaviour {
return this.conditionallyFullOpaque;
}
// Paper end
+ // Paper start
+ private long blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_SPECIAL_BLOCK;
+
+ public final long getBlockCollisionBehavior() {
+ return this.blockCollisionBehavior;
+ }
+ // Paper end
public void initCache() {
this.fluid = this.getBlock().getFluidState(this.asState()); // Paper - moved from getFluid()
@@ -726,7 +733,35 @@ public abstract class BlockBehaviour {
}
this.shapeExceedsCube = this.cache == null || this.cache.largeCollisionShape; // Paper - moved from actual method to here
this.opacityIfCached = this.cache == null || this.isConditionallyFullOpaque() ? -1 : this.cache.lightBlock; // Paper - cache opacity for light
-
+ // TODO optimise light
+ // Paper start
+ if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(this)) {
+ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_SPECIAL_BLOCK;
+ } else {
+ try {
+ // There is NOTHING HACKY ABOUT THIS AT ALLLLLLLLLLLLLLL
+ VoxelShape constantShape = this.getCollisionShape(null, null, null);
+ if (constantShape == null) {
+ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_UNKNOWN_BLOCK;
+ } else {
+ constantShape = constantShape.optimize();
+ if (constantShape.isEmpty()) {
+ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_EMPTY_BLOCK;
+ } else {
+ final List<net.minecraft.world.phys.AABB> boxes = constantShape.toAabbs();
+ if (constantShape == net.minecraft.world.phys.shapes.Shapes.getFullUnoptimisedCube() || (boxes.size() == 1 && boxes.get(0).equals(net.minecraft.world.phys.shapes.Shapes.BLOCK_OPTIMISED.aabb))) {
+ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_FULL_BLOCK;
+ } else {
+ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_UNKNOWN_BLOCK;
+ }
+ }
+ }
+ } catch (final Error error) {
+ throw error;
+ } catch (final Throwable throwable) {
+ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_UNKNOWN_BLOCK;
+ }
+ }
+ // Paper end
}
public Block getBlock() {
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
index d5ceebee36885c6470917bc1d0952733e983f030..e962db693510dc261d6456706a459929369f2510 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -43,6 +43,110 @@ public class LevelChunkSection {
this.biomes = new PalettedContainer<>(biomeRegistry, (Biome) biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes
}
+ // Paper start
+ protected int specialCollidingBlocks;
+ // blockIndex = x | (z << 4) | (y << 8)
+ private long[] knownBlockCollisionData;
+
+ private long[] initKnownDataField() {
+ return this.knownBlockCollisionData = new long[16 * 16 * 16 * 2 / Long.SIZE];
+ }
+
+ public final boolean hasSpecialCollidingBlocks() {
+ return this.specialCollidingBlocks != 0;
+ }
+
+ public static long getKnownBlockInfo(final int blockIndex, final long value) {
+ final int valueShift = (blockIndex & (Long.SIZE / 2 - 1));
+
+ return (value >>> (valueShift << 1)) & 0b11L;
+ }
+
+ public final long getKnownBlockInfo(final int blockIndex) {
+ if (this.knownBlockCollisionData == null) {
+ return 0L;
+ }
+
+ final int arrayIndex = (blockIndex >>> (6 - 1)); // blockIndex / (64/2)
+ final int valueShift = (blockIndex & (Long.SIZE / 2 - 1));
+
+ final long value = this.knownBlockCollisionData[arrayIndex];
+
+ return (value >>> (valueShift << 1)) & 0b11L;
+ }
+
+ // important detail: this returns 32 values, one for localZ = localZ & (~1) and one for localZ = localZ | 1
+ // the even localZ is the lower 32 bits, the odd is the upper 32 bits
+ public final long getKnownBlockInfoHorizontalRaw(final int localY, final int localZ) {
+ if (this.knownBlockCollisionData == null) {
+ return 0L;
+ }
+
+ final int horizontalIndex = (localZ << 4) | (localY << 8);
+ return this.knownBlockCollisionData[horizontalIndex >>> (6 - 1)];
+ }
+
+ private void initBlockCollisionData() {
+ this.specialCollidingBlocks = 0;
+ // In 1.18 all sections will be initialised, whether or not they have blocks (fucking stupid btw)
+ // This means we can't aggressively initialise the backing long[], or else memory usage will just skyrocket.
+ // So only init if we contain non-empty blocks.
+ if (this.nonEmptyBlockCount == 0) {
+ this.knownBlockCollisionData = null;
+ return;
+ }
+ this.initKnownDataField();
+ for (int index = 0; index < (16 * 16 * 16); ++index) {
+ final BlockState state = this.states.get(index);
+ this.setKnownBlockInfo(index, state);
+ if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(state)) {
+ ++this.specialCollidingBlocks;
+ }
+ }
+ }
+
+ // only use for initBlockCollisionData
+ private void setKnownBlockInfo(final int blockIndex, final BlockState blockState) {
+ final int arrayIndex = (blockIndex >>> (6 - 1)); // blockIndex / (64/2)
+ final int valueShift = (blockIndex & (Long.SIZE / 2 - 1)) << 1;
+
+ long value = this.knownBlockCollisionData[arrayIndex];
+
+ value &= ~(0b11L << valueShift);
+ value |= blockState.getBlockCollisionBehavior() << valueShift;
+
+ this.knownBlockCollisionData[arrayIndex] = value;
+ }
+
+ public void updateKnownBlockInfo(final int blockIndex, final BlockState from, final BlockState to) {
+ if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(from)) {
+ --this.specialCollidingBlocks;
+ }
+ if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(to)) {
+ ++this.specialCollidingBlocks;
+ }
+
+ if (this.nonEmptyBlockCount == 0) {
+ this.knownBlockCollisionData = null;
+ return;
+ }
+
+ if (this.knownBlockCollisionData == null) {
+ this.initKnownDataField();
+ }
+
+ final int arrayIndex = (blockIndex >>> (6 - 1)); // blockIndex / (64/2)
+ final int valueShift = (blockIndex & (Long.SIZE / 2 - 1)) << 1;
+
+ long value = this.knownBlockCollisionData[arrayIndex];
+
+ value &= ~(0b11L << valueShift);
+ value |= to.getBlockCollisionBehavior() << valueShift;
+
+ this.knownBlockCollisionData[arrayIndex] = value;
+ }
+ // Paper end
+
public static int getBottomBlockY(int chunkPos) {
return chunkPos << 4;
}
@@ -67,8 +171,8 @@ public class LevelChunkSection {
return this.setBlockState(x, y, z, state, true);
}
- public BlockState setBlockState(int x, int y, int z, BlockState state, boolean lock) {
- BlockState iblockdata1;
+ public BlockState setBlockState(int x, int y, int z, BlockState state, boolean lock) { // Paper - state -> new state
+ BlockState iblockdata1; // Paper - iblockdata1 -> oldState
if (lock) {
iblockdata1 = (BlockState) this.states.getAndSet(x, y, z, state);
@@ -107,6 +211,7 @@ public class LevelChunkSection {
++this.tickingFluidCount;
}
+ this.updateKnownBlockInfo(x | (z << 4) | (y << 8), iblockdata1, state); // Paper
return iblockdata1;
}
@@ -158,6 +263,7 @@ public class LevelChunkSection {
}
});
+ this.initBlockCollisionData(); // Paper
}
public PalettedContainer<BlockState> getStates() {
diff --git a/src/main/java/net/minecraft/world/phys/AABB.java b/src/main/java/net/minecraft/world/phys/AABB.java
index 120498a39b7ca7aee9763084507508d4a1c425aa..68cc6f2a78a06293a29317fda72ab3ee79b3533a 100644
--- a/src/main/java/net/minecraft/world/phys/AABB.java

View file

@ -10,10 +10,10 @@ This patch also prevents the saving/unloading of POI data when
world saving is disabled.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 02bcffcdde33088e43a7771924b2d2bf47badd09..e97e0dbc094963f0976590c2700686647f6dcc8a 100644
index 29fdc1b991c67006a13231abbbe50e201744b5c2..79dbffe899dd7e31af648302c557e2993f170623 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -811,6 +811,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -812,6 +812,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
// Paper end
}
@ -21,7 +21,7 @@ index 02bcffcdde33088e43a7771924b2d2bf47badd09..e97e0dbc094963f0976590c270068664
this.updatingChunks.queueUpdate(pos, holder); // Paper - Don't copy
this.modified = true;
@@ -956,7 +957,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -957,7 +958,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
gameprofilerfiller.pop();
}
@ -30,7 +30,7 @@ index 02bcffcdde33088e43a7771924b2d2bf47badd09..e97e0dbc094963f0976590c270068664
private void processUnloads(BooleanSupplier shouldKeepTicking) {
LongIterator longiterator = this.toDrop.iterator();
@@ -1019,6 +1020,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1021,6 +1022,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z);
}
// Paper end
@ -38,7 +38,7 @@ index 02bcffcdde33088e43a7771924b2d2bf47badd09..e97e0dbc094963f0976590c270068664
if (ichunkaccess instanceof LevelChunk) {
((LevelChunk) ichunkaccess).setLoaded(false);
}
@@ -1047,6 +1049,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1049,6 +1051,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
for (int index = 0, len = this.regionManagers.size(); index < len; ++index) {
this.regionManagers.get(index).removeChunk(holder.pos.x, holder.pos.z);
}
@ -46,7 +46,7 @@ index 02bcffcdde33088e43a7771924b2d2bf47badd09..e97e0dbc094963f0976590c270068664
} // Paper end
} finally { this.unloadingPlayerChunk = unloadingBefore; } // Paper - do not allow ticket level changes while unloading chunks
@@ -1123,6 +1126,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1125,6 +1128,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
this.poiManager.loadInData(pos, chunkHolder.poiData);
chunkHolder.tasks.forEach(Runnable::run);

View file

@ -5,10 +5,10 @@ Subject: [PATCH] Fix int overflow in chunk range check
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 387b0e9b862621e7d0c1179f348e07c25f1ee9c0..2e6e86439173ebdb13b9cebd1e266e91335c1e2d 100644
index 79dbffe899dd7e31af648302c557e2993f170623..6a035b173cf0d288b2912f568078fede45d138f2 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -621,9 +621,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -622,9 +622,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public static boolean isChunkInRange(int x1, int z1, int x2, int z2, int distance) {
int j1 = Math.max(0, Math.abs(x1 - x2) - 1);
int k1 = Math.max(0, Math.abs(z1 - z2) - 1);

View file

@ -0,0 +1,37 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Tue, 28 Dec 2021 07:19:01 -0800
Subject: [PATCH] Execute chunk tasks fairly for worlds while waiting for next
tick
Currently, only the first world would have had tasks executed.
This might result in chunks loading far slower in the nether,
for example.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index eab93e1e3712c0a01cac187bf5944818c813d665..1674deebbeab0995ed7acacf8052e1daf4d2a7bc 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1392,6 +1392,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.executeMidTickTasks(); // Paper - execute chunk tasks mid tick
return true;
} else {
+ boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
if (this.haveTime()) {
Iterator iterator = this.getAllLevels().iterator();
@@ -1399,12 +1400,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
ServerLevel worldserver = (ServerLevel) iterator.next();
if (worldserver.getChunkSource().pollTask()) {
- return true;
+ ret = true; // Paper - force execution of all worlds, do not just bias the first
}
}
}
- return false;
+ return ret; // Paper - force execution of all worlds, do not just bias the first
}
}

View file

@ -15,10 +15,10 @@ will be more effective, since more time will be allocated
to actually processing chunk tasks vs the ticket level updates.
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
index 0b34536cdffab31a717b613042d7098109846586..24fa3d847f7c0aec52133ffc658cd90a602a44d5 100644
index fd379155a67794288f7cdae3250767bc3615d421..d278e5b8b3386044e0fbc13add369794fd6f9cd7 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
@@ -36,6 +36,7 @@ import net.minecraft.world.level.chunk.LevelChunk;
@@ -37,6 +37,7 @@ import net.minecraft.world.level.chunk.LevelChunk;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -26,17 +26,17 @@ index 0b34536cdffab31a717b613042d7098109846586..24fa3d847f7c0aec52133ffc658cd90a
public abstract class DistanceManager {
static final Logger LOGGER = LogManager.getLogger();
@@ -44,7 +45,7 @@ public abstract class DistanceManager {
private static final int INITIAL_TICKET_LIST_CAPACITY = 4;
@@ -47,7 +48,7 @@ public abstract class DistanceManager {
private static final int BLOCK_TICKING_LEVEL_THRESHOLD = 33;
final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap();
public final Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> tickets = new Long2ObjectOpenHashMap();
- private final DistanceManager.ChunkTicketTracker ticketTracker = new DistanceManager.ChunkTicketTracker();
+ //private final DistanceManager.ChunkTicketTracker ticketTracker = new DistanceManager.ChunkTicketTracker(); // Paper - replace ticket level propagator
public static final int MOB_SPAWN_RANGE = 8; // private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); // Paper - no longer used
//private final TickingTracker tickingTicketsTracker = new TickingTracker(); // Paper - no longer used
//private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(33); // Paper - no longer used
// Paper start use a queue, but still keep unique requirement
@@ -77,6 +78,46 @@ public abstract class DistanceManager {
this.mainThreadExecutor = mainThreadExecutor;
@@ -82,6 +83,46 @@ public abstract class DistanceManager {
this.chunkMap = chunkMap; // Paper
}
+ // Paper start - replace ticket level propagator
@ -82,22 +82,23 @@ index 0b34536cdffab31a717b613042d7098109846586..24fa3d847f7c0aec52133ffc658cd90a
protected void purgeStaleTickets() {
++this.ticketTickCounter;
ObjectIterator objectiterator = this.tickets.long2ObjectEntrySet().fastIterator();
@@ -87,7 +128,7 @@ public abstract class DistanceManager {
if ((entry.getValue()).removeIf((ticket) -> { // CraftBukkit - decompile error
return ticket.timedOut(this.ticketTickCounter);
})) {
@@ -116,7 +157,7 @@ public abstract class DistanceManager {
}
if (flag) {
- this.ticketTracker.update(entry.getLongKey(), DistanceManager.getTicketLevelAt((SortedArraySet) entry.getValue()), false);
+ this.updateTicketLevel(entry.getLongKey(), getTicketLevelAt(entry.getValue())); // Paper - replace ticket level propagator
}
if (((SortedArraySet) entry.getValue()).isEmpty()) {
@@ -110,60 +151,93 @@ public abstract class DistanceManager {
@@ -139,61 +180,94 @@ public abstract class DistanceManager {
@Nullable
protected abstract ChunkHolder updateChunkScheduling(long pos, int level, @Nullable ChunkHolder holder, int k);
+ protected long ticketLevelUpdateCount; // Paper - replace ticket level propagator
public boolean runAllUpdates(ChunkMap playerchunkmap) {
public boolean runAllUpdates(ChunkMap chunkStorage) {
//this.f.a(); // Paper - no longer used
//this.tickingTicketsTracker.runAllUpdates(); // Paper - no longer used
org.spigotmc.AsyncCatcher.catchOp("DistanceManagerTick"); // Paper
//this.playerTicketManager.runAllUpdates(); // Paper - no longer used
- int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE);
@ -114,7 +115,7 @@ index 0b34536cdffab31a717b613042d7098109846586..24fa3d847f7c0aec52133ffc658cd90a
- while(!this.pendingChunkUpdates.isEmpty()) {
- ChunkHolder remove = this.pendingChunkUpdates.remove();
- remove.isUpdateQueued = false;
- remove.updateFutures(playerchunkmap, this.mainThreadExecutor);
- remove.updateFutures(chunkStorage, this.mainThreadExecutor);
- }
- } finally { this.pollingPendingChunkUpdates = false; } // Paper - Chunk priority
- // Paper end
@ -142,23 +143,23 @@ index 0b34536cdffab31a717b613042d7098109846586..24fa3d847f7c0aec52133ffc658cd90a
+ // not loaded and it shouldn't be loaded!
+ continue;
+ }
+
+ int currentLevel = chunk == null ? ChunkMap.MAX_CHUNK_DISTANCE + 1 : chunk.getTicketLevel();
+
+ if (currentLevel == newLevel) {
+ // nothing to do
+ continue;
+ }
- if (this.getTickets(j).stream().anyMatch((ticket) -> {
- return ticket.getType() == TicketType.PLAYER;
- })) {
- ChunkHolder playerchunk = playerchunkmap.getUpdatingChunkIfPresent(j);
+ this.updateChunkScheduling(key, newLevel, chunk, currentLevel);
+ }
- ChunkHolder playerchunk = chunkStorage.getUpdatingChunkIfPresent(j);
+ int currentLevel = chunk == null ? ChunkMap.MAX_CHUNK_DISTANCE + 1 : chunk.getTicketLevel();
- if (playerchunk == null) {
- throw new IllegalStateException();
+ if (currentLevel == newLevel) {
+ // nothing to do
+ continue;
+ }
+
+ this.updateChunkScheduling(key, newLevel, chunk, currentLevel);
+ }
+
+ long recursiveCheck = ++this.ticketLevelUpdateCount;
+ while (!this.ticketLevelUpdates.isEmpty()) {
+ long key = this.ticketLevelUpdates.firstLongKey();
@ -187,7 +188,7 @@ index 0b34536cdffab31a717b613042d7098109846586..24fa3d847f7c0aec52133ffc658cd90a
+ continue;
+ }
+
+ chunk.updateFutures(playerchunkmap, this.mainThreadExecutor);
+ chunk.updateFutures(chunkStorage, this.mainThreadExecutor);
+ if (recursiveCheck != this.ticketLevelUpdateCount) {
+ // back to the start, we must create player chunks and update the ticket level fields before
+ // processing the actual level updates
@ -207,7 +208,7 @@ index 0b34536cdffab31a717b613042d7098109846586..24fa3d847f7c0aec52133ffc658cd90a
+ }
- return flag;
+ pendingUpdate.updateFutures(playerchunkmap, this.mainThreadExecutor);
+ pendingUpdate.updateFutures(chunkStorage, this.mainThreadExecutor);
+ }
+ } finally {
+ this.pollingPendingChunkUpdates = oldPolling;
@ -219,7 +220,7 @@ index 0b34536cdffab31a717b613042d7098109846586..24fa3d847f7c0aec52133ffc658cd90a
}
boolean pollingPendingChunkUpdates = false; // Paper - Chunk priority
@@ -175,7 +249,7 @@ public abstract class DistanceManager {
@@ -205,7 +279,7 @@ public abstract class DistanceManager {
ticket1.setCreatedTick(this.ticketTickCounter);
if (ticket.getTicketLevel() < j) {
@ -228,7 +229,7 @@ index 0b34536cdffab31a717b613042d7098109846586..24fa3d847f7c0aec52133ffc658cd90a
}
return ticket == ticket1; // CraftBukkit
@@ -219,7 +293,7 @@ public abstract class DistanceManager {
@@ -249,7 +323,7 @@ public abstract class DistanceManager {
// Paper start - Chunk priority
int newLevel = getTicketLevelAt(arraysetsorted);
if (newLevel > oldLevel) {
@ -237,7 +238,7 @@ index 0b34536cdffab31a717b613042d7098109846586..24fa3d847f7c0aec52133ffc658cd90a
}
// Paper end
return removed; // CraftBukkit
@@ -507,7 +581,7 @@ public abstract class DistanceManager {
@@ -549,7 +623,7 @@ public abstract class DistanceManager {
SortedArraySet<Ticket<?>> tickets = entry.getValue();
if (tickets.remove(target)) {
// copied from removeTicket