Archiviert
13
0

More Improvements to Chunks

Fixed issues where urgent and prioritized chunks didn't actually
always get their priority boosted correctly....

Properly deprioritize non ticking chunks.
Limit recursion on watchdog prints to stop flooding as much
Remove neighbor priorities from watchdog to reduce information
reduce synchronization duration so that watch dog won't block main should main actually wake up
probably fixed a deadlock risk in watchdog printing also that was leading to crashes
fixed chunk holder enqueues not being processed correctly
added async catchers in some locations that should not be ran async

Fixed upstream bug where VITAL callbacks that must run on main actually could
sometimes run on the server thread pool causing alot of these nasty bugs we've seen lately!

This build will provide massive improvements to stability as well as even faster
sync chunk load/gens now that priority is correctly set.

Fixes #3435
Dieser Commit ist enthalten in:
Aikar 2020-05-22 19:03:48 -04:00
Ursprung e1c451968c
Commit 7c001d64a5
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 401ADFC9891FAAFE
8 geänderte Dateien mit 268 neuen und 146 gelöschten Zeilen

Datei anzeigen

@ -3265,14 +3265,15 @@ index 75308712d0642d5ab168de653023349df8aee5ed..aa7501d366b15e7f7f64b7d98a1dccff
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..b40cd1fad5c9e2f0f85c87a559caf2b780814017
index 0000000000000000000000000000000000000000..aaa6e33b0e5df2549e4f989501bacfd1ab4ad063
--- /dev/null
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -0,0 +1,487 @@
@@ -0,0 +1,532 @@
+package net.minecraft.server;
+
+import com.destroystokyo.paper.block.TargetBlockInfo;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.bukkit.Location;
+import org.bukkit.block.BlockFace;
+import org.bukkit.craftbukkit.CraftWorld;
@ -3290,6 +3291,7 @@ index 0000000000000000000000000000000000000000..b40cd1fad5c9e2f0f85c87a559caf2b7
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
@ -3317,6 +3319,12 @@ index 0000000000000000000000000000000000000000..b40cd1fad5c9e2f0f85c87a559caf2b7
+ };
+ }
+
+ public static <T> Runnable once(List<T> list, Consumer<T> cb) {
+ return once(() -> {
+ list.forEach(cb);
+ });
+ }
+
+ private static Runnable makeCleanerCallback(Runnable run) {
+ return once(() -> cleanerExecutor.execute(run));
+ }
@ -3384,19 +3392,16 @@ index 0000000000000000000000000000000000000000..b40cd1fad5c9e2f0f85c87a559caf2b7
+ return list;
+ }
+
+ public static long getCoordinateKey(final BlockPosition blockPos) {
+ return ((long)(blockPos.getZ() >> 4) << 32) | ((blockPos.getX() >> 4) & 0xFFFFFFFFL);
+ }
+
+ public static long getCoordinateKey(final Entity entity) {
+ return ((long)(MCUtil.fastFloor(entity.locZ()) >> 4) << 32) | ((MCUtil.fastFloor(entity.locX()) >> 4) & 0xFFFFFFFFL);
+ }
+
+ public static int fastFloor(double x) {
+ int truncated = (int)x;
+ return x < (double)truncated ? truncated - 1 : truncated;
+ }
+
+ public static int fastFloor(float x) {
+ int truncated = (int)x;
+ return x < (double)truncated ? truncated - 1 : truncated;
+ }
+
+ public static float normalizeYaw(float f) {
+ float f1 = f % 360.0F;
+
@ -3411,9 +3416,31 @@ index 0000000000000000000000000000000000000000..b40cd1fad5c9e2f0f85c87a559caf2b7
+ return f1;
+ }
+
+ public static int fastFloor(float x) {
+ int truncated = (int)x;
+ return x < (double)truncated ? truncated - 1 : truncated;
+ /**
+ * Quickly generate a stack trace for current location
+ *
+ * @return Stacktrace
+ */
+ public static String stack() {
+ return ExceptionUtils.getFullStackTrace(new Throwable());
+ }
+
+ /**
+ * Quickly generate a stack trace for current location with message
+ *
+ * @param str
+ * @return Stacktrace
+ */
+ public static String stack(String str) {
+ return ExceptionUtils.getFullStackTrace(new Throwable(str));
+ }
+
+ public static long getCoordinateKey(final BlockPosition blockPos) {
+ return ((long)(blockPos.getZ() >> 4) << 32) | ((blockPos.getX() >> 4) & 0xFFFFFFFFL);
+ }
+
+ public static long getCoordinateKey(final Entity entity) {
+ return ((long)(MCUtil.fastFloor(entity.locZ()) >> 4) << 32) | ((MCUtil.fastFloor(entity.locX()) >> 4) & 0xFFFFFFFFL);
+ }
+
+ public static long getCoordinateKey(final ChunkCoordIntPair pair) {
@ -3466,6 +3493,24 @@ index 0000000000000000000000000000000000000000..b40cd1fad5c9e2f0f85c87a559caf2b7
+
+ private MCUtil() {}
+
+ public static final java.util.concurrent.Executor MAIN_EXECUTOR = (run) -> {
+ if (!isMainThread()) {
+ MinecraftServer.getServer().execute(run);
+ } else {
+ run.run();
+ }
+ };
+
+ public static <T> CompletableFuture<T> ensureMain(CompletableFuture<T> future) {
+ return future.thenApplyAsync(r -> r, MAIN_EXECUTOR);
+ }
+
+ public static <T> void thenOnMain(CompletableFuture<T> future, Consumer<T> consumer) {
+ future.thenAcceptAsync(consumer, MAIN_EXECUTOR);
+ }
+ public static <T> void thenOnMain(CompletableFuture<T> future, BiConsumer<T, Throwable> consumer) {
+ future.whenCompleteAsync(consumer, MAIN_EXECUTOR);
+ }
+
+ public static boolean isMainThread() {
+ return MinecraftServer.getServer().isMainThread();

Datei anzeigen

@ -4,43 +4,6 @@ Date: Tue, 27 Dec 2016 15:02:42 -0500
Subject: [PATCH] String based Action Bar API
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index b40cd1fad5c9e2f0f85c87a559caf2b780814017..f9a7a1e9eea67bafb85c0ed88e96abb8e45f6c81 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -2,6 +2,7 @@ package net.minecraft.server;
import com.destroystokyo.paper.block.TargetBlockInfo;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.apache.commons.lang.exception.ExceptionUtils;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.craftbukkit.CraftWorld;
@@ -195,6 +196,24 @@ public final class MCUtil {
private MCUtil() {}
+ /**
+ * Quickly generate a stack trace for current location
+ *
+ * @return Stacktrace
+ */
+ public static String stack() {
+ return ExceptionUtils.getFullStackTrace(new Throwable());
+ }
+
+ /**
+ * Quickly generate a stack trace for current location with message
+ *
+ * @param str
+ * @return Stacktrace
+ */
+ public static String stack(String str) {
+ return ExceptionUtils.getFullStackTrace(new Throwable(str));
+ }
public static boolean isMainThread() {
return MinecraftServer.getServer().isMainThread();
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 764a8ef952b6f3a38ae8430c0648ad1694aa89b9..d349f0c87bfad19cf0bddb4709f1d7b0dd4b4a36 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java

Datei anzeigen

@ -403,21 +403,22 @@ index 0000000000000000000000000000000000000000..3aceb0ea8a1a3ed94dd8a9e954c52ecd
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index f9a7a1e9eea67bafb85c0ed88e96abb8e45f6c81..8ca1a4719d934db31d57b34cf7050acc5a1a7048 100644
index aaa6e33b0e5df2549e4f989501bacfd1ab4ad063..8ebe5a2e2678fccb17aced57f6fd1e52c17935db 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -1,7 +1,10 @@
@@ -1,8 +1,11 @@
package net.minecraft.server;
import com.destroystokyo.paper.block.TargetBlockInfo;
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
+import com.destroystokyo.paper.profile.PlayerProfile;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.mojang.authlib.GameProfile;
import org.apache.commons.lang.exception.ExceptionUtils;
+import com.mojang.authlib.GameProfile;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
@@ -329,6 +332,10 @@ public final class MCUtil {
import org.bukkit.craftbukkit.CraftWorld;
@@ -355,6 +358,10 @@ public final class MCUtil {
return run.get();
}

Datei anzeigen

@ -39,10 +39,10 @@ index 0f74ec89b3e85c918c95f9d8fef6d68403ed1107..4609e402b419ed21e17ad34d02dca55b
this.setCustomNameVisible(nbttagcompound.getBoolean("CustomNameVisible"));
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index 8ca1a4719d934db31d57b34cf7050acc5a1a7048..206d04dcce1d7d074cf7151a083bdc626b0b8e07 100644
index 8ebe5a2e2678fccb17aced57f6fd1e52c17935db..45179aec0d91149ba9cc4d95e65c489ce695053d 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -510,4 +510,19 @@ public final class MCUtil {
@@ -536,4 +536,19 @@ public final class MCUtil {
return null;
}
}

Datei anzeigen

@ -198,13 +198,13 @@ index 8c6550433c20c54cbe390219821ce393c5720da8..e6d08756f76360b29b29f18305e5ec84
public final ChunkGenerator<?> chunkGenerator;
private final WorldServer world;
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index 206d04dcce1d7d074cf7151a083bdc626b0b8e07..f75f48a3d0b0bc1da3c5ae3b3cf20b64f1e8288a 100644
index 45179aec0d91149ba9cc4d95e65c489ce695053d..20f54baacebe98435539d4cbef41f182040db2e9 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -4,7 +4,13 @@ import com.destroystokyo.paper.block.TargetBlockInfo;
import com.destroystokyo.paper.profile.CraftPlayerProfile;
@@ -5,7 +5,13 @@ import com.destroystokyo.paper.profile.CraftPlayerProfile;
import com.destroystokyo.paper.profile.PlayerProfile;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.commons.lang.exception.ExceptionUtils;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gson.internal.Streams;
@ -212,9 +212,9 @@ index 206d04dcce1d7d074cf7151a083bdc626b0b8e07..f75f48a3d0b0bc1da3c5ae3b3cf20b64
import com.mojang.authlib.GameProfile;
+import com.mojang.datafixers.util.Either;
+import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.craftbukkit.CraftWorld;
@@ -14,8 +20,11 @@ import org.spigotmc.AsyncCatcher;
import javax.annotation.Nonnull;
@ -227,7 +227,7 @@ index 206d04dcce1d7d074cf7151a083bdc626b0b8e07..f75f48a3d0b0bc1da3c5ae3b3cf20b64
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;
@@ -525,4 +534,170 @@ public final class MCUtil {
@@ -551,4 +560,170 @@ public final class MCUtil {
return null;
}

Datei anzeigen

@ -1847,10 +1847,10 @@ index 0000000000000000000000000000000000000000..1dfa8abfd869ca97e4cc566d44e509b4
+}
diff --git a/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..a5f4cdaf06bfbb0dd957db9a1335c17b073d646d
index 0000000000000000000000000000000000000000..b5c2e1f4a2b5fdcaa6bb01f4b3b6847cd5b73ae8
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java
@@ -0,0 +1,509 @@
@@ -0,0 +1,511 @@
+package com.destroystokyo.paper.io.chunk;
+
+import com.destroystokyo.paper.io.PaperFileIOThread;
@ -1929,22 +1929,20 @@ index 0000000000000000000000000000000000000000..a5f4cdaf06bfbb0dd957db9a1335c17b
+ }
+ }
+
+ public static String getChunkWaitInfo() {
+ private static ChunkInfo[] getChunkInfos() {
+ ChunkInfo[] chunks;
+ synchronized (WAITING_CHUNKS) {
+ return WAITING_CHUNKS.toString();
+ chunks = WAITING_CHUNKS.toArray(new ChunkInfo[0]);
+ }
+ return chunks;
+ }
+
+ public static void dumpAllChunkLoadInfo() {
+ synchronized (WAITING_CHUNKS) {
+ if (WAITING_CHUNKS.isEmpty()) {
+ return;
+ }
+
+ ChunkInfo[] chunks = getChunkInfos();
+ if (chunks.length > 0) {
+ PaperFileIOThread.LOGGER.log(Level.ERROR, "Chunk wait task info below: ");
+ Set<PlayerChunk> seenChunks = new HashSet<>();
+
+ for (final ChunkInfo chunkInfo : WAITING_CHUNKS) {
+ for (final ChunkInfo chunkInfo : chunks) {
+ final long key = IOUtil.getCoordinateKey(chunkInfo.chunkX, chunkInfo.chunkZ);
+ final ChunkLoadTask loadTask = chunkInfo.world.asyncChunkTaskManager.chunkLoadTasks.get(key);
+ final ChunkSaveTask saveTask = chunkInfo.world.asyncChunkTaskManager.chunkSaveTasks.get(key);
@ -1955,18 +1953,22 @@ index 0000000000000000000000000000000000000000..a5f4cdaf06bfbb0dd957db9a1335c17b
+ // log current status of chunk to indicate whether we're waiting on generation or loading
+ net.minecraft.server.PlayerChunk chunkHolder = chunkInfo.world.getChunkProvider().playerChunkMap.getVisibleChunk(key);
+
+ dumpChunkInfo(seenChunks, chunkHolder, chunkInfo.chunkX, chunkInfo.chunkZ);
+ dumpChunkInfo(new HashSet<>(), chunkHolder, chunkInfo.chunkX, chunkInfo.chunkZ);
+ }
+ }
+ }
+
+ static void dumpChunkInfo(Set<PlayerChunk> seenChunks, PlayerChunk chunkHolder, int x, int z) {
+ dumpChunkInfo(seenChunks, chunkHolder, x, z, 0);
+ dumpChunkInfo(seenChunks, chunkHolder, x, z, 0, 1);
+ }
+ static void dumpChunkInfo(Set<PlayerChunk> seenChunks, PlayerChunk chunkHolder, int x, int z, int indent) {
+
+ static void dumpChunkInfo(Set<PlayerChunk> seenChunks, PlayerChunk chunkHolder, int x, int z, int indent, int maxDepth) {
+ if (seenChunks.contains(chunkHolder)) {
+ return;
+ }
+ if (indent > maxDepth) {
+ return;
+ }
+ seenChunks.add(chunkHolder);
+ String indentStr = StringUtils.repeat(" ", indent);
+ if (chunkHolder == null) {
@ -2987,10 +2989,10 @@ index 2f95174fcc467908808ed3f2dc956bdcafdc3558..134c76065bf382912e6c28d15449db3f
+// Paper end
}
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index f75f48a3d0b0bc1da3c5ae3b3cf20b64f1e8288a..0e01e5c2c008823355e370d0c9ced79130e5fb92 100644
index 20f54baacebe98435539d4cbef41f182040db2e9..9f8c0e10e42d233a8b74ee5a71fb8fb6ea8e7480 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -700,4 +700,9 @@ public final class MCUtil {
@@ -726,4 +726,9 @@ public final class MCUtil {
out.print(fileData);
}
}

Datei anzeigen

@ -70,10 +70,10 @@ index d53b34ba552771bf271131ce0a56ebb992ccc84c..a1b5e6b90fc93f83186cf3ebf3e15876
if (optional.isPresent()) {
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index 0e01e5c2c008823355e370d0c9ced79130e5fb92..d129c7f54d9f65fff6f512d8ff5f1c3866632603 100644
index 9f8c0e10e42d233a8b74ee5a71fb8fb6ea8e7480..0d1065688b19ceca9440bc8bf2bf65910f03fa46 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -602,7 +602,7 @@ public final class MCUtil {
@@ -628,7 +628,7 @@ public final class MCUtil {
WorldServer world = ((org.bukkit.craftbukkit.CraftWorld)bukkitWorld).getHandle();
PlayerChunkMap chunkMap = world.getChunkProvider().playerChunkMap;

Datei anzeigen

@ -23,7 +23,7 @@ Chunks in front of the player have higher priority, to help with
fast traveling players keep up with their movement.
diff --git a/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java
index a5f4cdaf06bfbb0dd957db9a1335c17b073d646d..3a4e7d8ce0a4591f56ec08ebe1c3bbb4f046b128 100644
index b5c2e1f4a2b5fdcaa6bb01f4b3b6847cd5b73ae8..6209b33d8497ec56bbde507e523db0649c66f590 100644
--- a/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java
+++ b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java
@@ -4,7 +4,10 @@ import com.destroystokyo.paper.io.PaperFileIOThread;
@ -37,45 +37,48 @@ index a5f4cdaf06bfbb0dd957db9a1335c17b073d646d..3a4e7d8ce0a4591f56ec08ebe1c3bbb4
import net.minecraft.server.IAsyncTaskHandler;
import net.minecraft.server.IChunkAccess;
import net.minecraft.server.MinecraftServer;
@@ -125,6 +128,36 @@ public final class ChunkTaskManager {
@@ -106,7 +109,7 @@ public final class ChunkTaskManager {
}
static void dumpChunkInfo(Set<PlayerChunk> seenChunks, PlayerChunk chunkHolder, int x, int z) {
- dumpChunkInfo(seenChunks, chunkHolder, x, z, 0, 1);
+ dumpChunkInfo(seenChunks, chunkHolder, x, z, 0, 4);
}
static void dumpChunkInfo(Set<PlayerChunk> seenChunks, PlayerChunk chunkHolder, int x, int z, int indent, int maxDepth) {
@@ -127,6 +130,30 @@ public final class ChunkTaskManager {
PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Status - " + ((chunk == null) ? "null chunk" : chunk.getChunkStatus().toString()));
PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Ticket Status - " + PlayerChunk.getChunkStatus(chunkHolder.getTicketLevel()));
PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Holder Status - " + ((holderStatus == null) ? "null" : holderStatus.toString()));
+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Holder Priority - " + chunkHolder.getCurrentPriority());
+ synchronized (chunkHolder.neighborPriorities) {
+ if (!chunkHolder.neighborPriorities.isEmpty()) {
+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Neighbors Requested Priority: ");
+ for (Long2ObjectMap.Entry<Integer> entry : chunkHolder.neighborPriorities.long2ObjectEntrySet()) {
+ ChunkCoordIntPair r = new ChunkCoordIntPair(entry.getLongKey());
+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + " (" + r.x + "," + r.z + "): " + entry.getValue());
+
+ if (!chunkHolder.neighbors.isEmpty()) {
+ if (indent >= maxDepth) {
+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Neighbors: (Can't show, too deeply nested)");
+ return;
+ }
+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Neighbors: ");
+ for (PlayerChunk neighbor : chunkHolder.neighbors.keySet()) {
+ ChunkStatus status = neighbor.getChunkHolderStatus();
+ if (status != null && status.isAtLeastStatus(PlayerChunk.getChunkStatus(neighbor.getTicketLevel()))) {
+ continue;
+ }
+ int nx = neighbor.location.x;
+ int nz = neighbor.location.z;
+ if (seenChunks.contains(neighbor)) {
+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + " " + nx + "," + nz + " in " + chunkHolder.getWorld().getWorld().getName() + " (CIRCULAR)");
+ continue;
+ }
+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + " " + nx + "," + nz + " in " + chunkHolder.getWorld().getWorld().getName() + ":");
+ dumpChunkInfo(seenChunks, neighbor, nx, nz, indent + 1, maxDepth);
+ }
+ }
+
+ synchronized (chunkHolder.neighbors) {
+ if (!chunkHolder.neighbors.isEmpty()) {
+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Neighbors: ");
+ for (PlayerChunk neighbor : chunkHolder.neighbors.keySet()) {
+ ChunkStatus status = neighbor.getChunkHolderStatus();
+ if (status != null && status.isAtLeastStatus(PlayerChunk.getChunkStatus(neighbor.getTicketLevel()))) {
+ continue;
+ }
+ int nx = neighbor.location.x;
+ int nz = neighbor.location.z;
+ if (seenChunks.contains(neighbor)) {
+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + " " + nx + "," + nz + " in " + chunkHolder.getWorld().getWorld().getName() + " (CIRCULAR)");
+ continue;
+ }
+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + " " + nx + "," + nz + " in " + chunkHolder.getWorld().getWorld().getName() + ":");
+ dumpChunkInfo(seenChunks, neighbor, nx, nz, indent + 1);
+ }
+ }
+ }
}
}
diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java
index 586a20fe5c77c2ad5fa26f337a94a16e21d8b5e2..6e422836f5a013d946965a2bb807c862cfc53912 100644
index 586a20fe5c77c2ad5fa26f337a94a16e21d8b5e2..771d879711988bbece02c8b375db786b733925b5 100644
--- a/src/main/java/net/minecraft/server/ChunkMapDistance.java
+++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java
@@ -23,6 +23,7 @@ import java.util.concurrent.Executor;
@ -94,7 +97,15 @@ index 586a20fe5c77c2ad5fa26f337a94a16e21d8b5e2..6e422836f5a013d946965a2bb807c862
return !arraysetsorted.isEmpty() ? ((Ticket) arraysetsorted.b()).b() : PlayerChunkMap.GOLDEN_TICKET + 1;
}
@@ -107,11 +109,13 @@ public abstract class ChunkMapDistance {
@@ -97,6 +99,7 @@ public abstract class ChunkMapDistance {
public boolean a(PlayerChunkMap playerchunkmap) {
//this.f.a(); // Paper - no longer used
+ AsyncCatcher.catchOp("DistanceManagerTick");
this.g.a();
int i = Integer.MAX_VALUE - this.e.a(Integer.MAX_VALUE);
boolean flag = i != 0;
@@ -107,11 +110,13 @@ public abstract class ChunkMapDistance {
// Paper start
if (!this.pendingChunkUpdates.isEmpty()) {
@ -108,7 +119,7 @@ index 586a20fe5c77c2ad5fa26f337a94a16e21d8b5e2..6e422836f5a013d946965a2bb807c862
// Paper end
return true;
} else {
@@ -147,14 +151,16 @@ public abstract class ChunkMapDistance {
@@ -147,14 +152,16 @@ public abstract class ChunkMapDistance {
return flag;
}
}
@ -126,7 +137,7 @@ index 586a20fe5c77c2ad5fa26f337a94a16e21d8b5e2..6e422836f5a013d946965a2bb807c862
this.e.b(i, ticket.b(), true);
}
@@ -162,6 +168,7 @@ public abstract class ChunkMapDistance {
@@ -162,6 +169,7 @@ public abstract class ChunkMapDistance {
}
private boolean removeTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
@ -134,11 +145,12 @@ index 586a20fe5c77c2ad5fa26f337a94a16e21d8b5e2..6e422836f5a013d946965a2bb807c862
ArraySetSorted<Ticket<?>> arraysetsorted = this.e(i);
boolean removed = false; // CraftBukkit
@@ -182,6 +189,59 @@ public abstract class ChunkMapDistance {
@@ -182,6 +190,82 @@ public abstract class ChunkMapDistance {
this.addTicketAtLevel(tickettype, chunkcoordintpair, i, t0);
}
+ // Paper start
+ public static final int PRIORITY_TICKET_LEVEL = 33;
+ public boolean markUrgent(ChunkCoordIntPair coords) {
+ return addPriorityTicket(coords, TicketType.URGENT, 30);
+ }
@ -150,16 +162,38 @@ index 586a20fe5c77c2ad5fa26f337a94a16e21d8b5e2..6e422836f5a013d946965a2bb807c862
+ private boolean addPriorityTicket(ChunkCoordIntPair coords, TicketType<ChunkCoordIntPair> ticketType, int priority) {
+ AsyncCatcher.catchOp("ChunkMapDistance::addPriorityTicket");
+ long pair = coords.pair();
+ Ticket<ChunkCoordIntPair> ticket = new Ticket<ChunkCoordIntPair>(ticketType, 34, coords);
+ ticket.priority = priority;
+
+ this.removeTicket(pair, ticket);
+ boolean added = this.addTicket(pair, ticket);
+ PlayerChunk updatingChunk = chunkMap.getUpdatingChunk(pair);
+ if (updatingChunk != null) {
+ chunkMap.queueHolderUpdate(updatingChunk);
+ boolean success;
+ if (!(success = updatePriorityTicket(coords, ticketType, priority))) {
+ Ticket<ChunkCoordIntPair> ticket = new Ticket<ChunkCoordIntPair>(ticketType, PRIORITY_TICKET_LEVEL, coords);
+ ticket.priority = priority;
+ success = this.addTicket(pair, ticket);
+ }
+ return added;
+
+ chunkMap.world.getChunkProvider().tickDistanceManager();
+ PlayerChunk updatingChunk = chunkMap.getUpdatingChunk(pair);
+ if (updatingChunk != null && updatingChunk.priorityBoost < priority) {
+ // May not be enqueued, enqueue it if not and tick distance manager
+ chunkMap.queueHolderUpdate(updatingChunk);
+ chunkMap.world.getChunkProvider().tickDistanceManager();
+ }
+ return success;
+ }
+
+ private boolean updatePriorityTicket(ChunkCoordIntPair coords, TicketType<ChunkCoordIntPair> type, int priority) {
+ ArraySetSorted<Ticket<?>> tickets = this.tickets.get(coords.pair());
+ if (tickets == null) {
+ return false;
+ }
+ for (Ticket<?> ticket : tickets) {
+ if (ticket.getTicketType() == type) {
+ // We only support increasing, not decreasing, too complicated
+ ticket.priority = Math.max(ticket.priority, priority);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public int getChunkPriority(ChunkCoordIntPair coords) {
@ -183,18 +217,18 @@ index 586a20fe5c77c2ad5fa26f337a94a16e21d8b5e2..6e422836f5a013d946965a2bb807c862
+
+ public void clearPriorityTickets(ChunkCoordIntPair coords) {
+ AsyncCatcher.catchOp("ChunkMapDistance::clearPriority");
+ this.removeTicket(coords.pair(), new Ticket<ChunkCoordIntPair>(TicketType.PRIORITY, 34, coords));
+ this.removeTicket(coords.pair(), new Ticket<ChunkCoordIntPair>(TicketType.PRIORITY, PRIORITY_TICKET_LEVEL, coords));
+ }
+
+ public void clearUrgent(ChunkCoordIntPair coords) {
+ AsyncCatcher.catchOp("ChunkMapDistance::clearUrgent");
+ this.removeTicket(coords.pair(), new Ticket<ChunkCoordIntPair>(TicketType.URGENT, 34, coords));
+ this.removeTicket(coords.pair(), new Ticket<ChunkCoordIntPair>(TicketType.URGENT, PRIORITY_TICKET_LEVEL, coords));
+ }
+ // Paper end
public <T> boolean addTicketAtLevel(TicketType<T> ticketType, ChunkCoordIntPair chunkcoordintpair, int level, T identifier) {
return this.addTicket(chunkcoordintpair.pair(), new Ticket<>(ticketType, level, identifier));
// CraftBukkit end
@@ -397,12 +457,14 @@ public abstract class ChunkMapDistance {
@@ -397,12 +481,14 @@ public abstract class ChunkMapDistance {
});
}, i, () -> {
@ -297,10 +331,10 @@ index 07a6fc3d88e7d44bfab7f3d6a0eef7dc132ab422..d60f659b368500e3a8c3305f99e60ffc
for (int i = 0; i < this.inventory.getSize(); ++i) {
ItemStack itemstack = this.inventory.getItem(i);
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index d129c7f54d9f65fff6f512d8ff5f1c3866632603..9b9536fba4a62c0153b921e678e6a9683bf2e37f 100644
index 0d1065688b19ceca9440bc8bf2bf65910f03fa46..8a349964578e07e5ed13f801c57de68459220da9 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -658,6 +658,7 @@ public final class MCUtil {
@@ -684,6 +684,7 @@ public final class MCUtil {
chunkData.addProperty("x", playerChunk.location.x);
chunkData.addProperty("z", playerChunk.location.z);
chunkData.addProperty("ticket-level", playerChunk.getTicketLevel());
@ -309,7 +343,7 @@ index d129c7f54d9f65fff6f512d8ff5f1c3866632603..9b9536fba4a62c0153b921e678e6a968
chunkData.addProperty("queued-for-unload", chunkMap.unloadQueue.contains(playerChunk.location.pair()));
chunkData.addProperty("status", status == null ? "unloaded" : status.toString());
diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..ee70efd4910fbbc489d4eb41342ece44f898c284 100644
index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..b8fe42e8123e972b1ec97b048c35d90118076e66 100644
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
@@ -26,8 +26,8 @@ public class PlayerChunk {
@ -345,7 +379,7 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..ee70efd4910fbbc489d4eb41342ece44
+ int priority = neighborPriority; // if we have a neighbor priority, use it
+ int myPriority = getMyPriority();
+
+ if (priority == -1 || priority > myPriority) {
+ if (priority == -1 || (ticketLevel <= 33 && priority > myPriority)) {
+ priority = myPriority;
+ }
+
@ -357,7 +391,7 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..ee70efd4910fbbc489d4eb41342ece44
+ return 1; // Urgent - ticket level isn't always 31 so 33-30 = 3
+ }
+ int basePriority = ticketLevel - priorityBoost;
+ if (ticketLevel >= 34 && priorityBoost == 0 && neighborPriorities.isEmpty()) {
+ if (ticketLevel >= 33 && priorityBoost == 0 && (neighborPriority >= 34 || neighborPriorities.isEmpty())) {
+ basePriority += 5;
+ }
+ return basePriority;
@ -460,7 +494,7 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..ee70efd4910fbbc489d4eb41342ece44
public PlayerChunk(ChunkCoordIntPair chunkcoordintpair, int i, LightEngine lightengine, PlayerChunk.c playerchunk_c, PlayerChunk.d playerchunk_d) {
this.statusFutures = new AtomicReferenceArray(PlayerChunk.CHUNK_STATUSES.size());
@@ -165,6 +288,12 @@ public class PlayerChunk {
@@ -165,6 +288,15 @@ public class PlayerChunk {
}
return null;
}
@ -469,11 +503,14 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..ee70efd4910fbbc489d4eb41342ece44
+ return status;
+ }
+ return CHUNK_STATUSES.get(status.getStatusIndex() + 1);
+ }
+ public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUncheckedMain(ChunkStatus chunkstatus) {
+ return MCUtil.ensureMain(getStatusFutureUnchecked(chunkstatus));
+ }
// Paper end
public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUnchecked(ChunkStatus chunkstatus) {
@@ -418,6 +547,7 @@ public class PlayerChunk {
@@ -418,6 +550,7 @@ public class PlayerChunk {
return this.n;
}
@ -481,7 +518,23 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..ee70efd4910fbbc489d4eb41342ece44
private void d(int i) {
this.n = i;
}
@@ -507,6 +637,7 @@ public class PlayerChunk {
@@ -436,7 +569,7 @@ public class PlayerChunk {
// CraftBukkit start
// ChunkUnloadEvent: Called before the chunk is unloaded: isChunkLoaded is still true and chunk can still be modified by plugins.
if (playerchunk_state.isAtLeast(PlayerChunk.State.BORDER) && !playerchunk_state1.isAtLeast(PlayerChunk.State.BORDER)) {
- this.getStatusFutureUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+ this.getStatusFutureUncheckedMain(ChunkStatus.FULL).thenAccept((either) -> { // Paper - ensure main
Chunk chunk = (Chunk)either.left().orElse(null);
if (chunk != null) {
playerchunkmap.callbackExecutor.execute(() -> {
@@ -501,12 +634,13 @@ public class PlayerChunk {
if (!flag2 && flag3) {
// Paper start - cache ticking ready status
int expectCreateCount = ++this.fullChunkCreateCount;
- this.fullChunkFuture = playerchunkmap.b(this); this.fullChunkFuture.thenAccept((either) -> {
+ this.fullChunkFuture = playerchunkmap.b(this); MCUtil.ensureMain(this.fullChunkFuture).thenAccept((either) -> { // Paper - ensure main
if (either.left().isPresent() && PlayerChunk.this.fullChunkCreateCount == expectCreateCount) {
// note: Here is a very good place to add callbacks to logic waiting on this.
Chunk fullChunk = either.left().get();
PlayerChunk.this.isFullChunkReady = true;
fullChunk.playerChunk = PlayerChunk.this;
@ -489,7 +542,25 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..ee70efd4910fbbc489d4eb41342ece44
}
@@ -581,8 +712,22 @@ public class PlayerChunk {
@@ -531,7 +665,7 @@ public class PlayerChunk {
if (!flag4 && flag5) {
// Paper start - cache ticking ready status
- this.tickingFuture = playerchunkmap.a(this); this.tickingFuture.thenAccept((either) -> {
+ this.tickingFuture = playerchunkmap.a(this); MCUtil.ensureMain(this.tickingFuture).thenAccept((either) -> { // Paper - ensure main
if (either.left().isPresent()) {
// note: Here is a very good place to add callbacks to logic waiting on this.
Chunk tickingChunk = either.left().get();
@@ -562,7 +696,7 @@ public class PlayerChunk {
}
// Paper start - cache ticking ready status
- this.entityTickingFuture = playerchunkmap.b(this.location); this.entityTickingFuture.thenAccept((either) -> {
+ this.entityTickingFuture = playerchunkmap.b(this.location); MCUtil.ensureMain(this.entityTickingFuture).thenAccept((either) -> { // Paper ensureMain
if (either.left().isPresent()) {
// note: Here is a very good place to add callbacks to logic waiting on this.
Chunk entityTickingChunk = either.left().get();
@@ -581,13 +715,29 @@ public class PlayerChunk {
this.entityTickingFuture.complete(PlayerChunk.UNLOADED_CHUNK); this.isEntityTickingReady = false; // Paper - cache chunk ticking stage
this.entityTickingFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
}
@ -507,14 +578,22 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..ee70efd4910fbbc489d4eb41342ece44
+ }
+ chunkMap.world.asyncChunkTaskManager.raisePriority(location.x, location.z, ioPriority);
+ }
+ this.w.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority
+ int neighborsPriority = getNeighborsPriority();
+ this.neighbors.forEach((neighbor, neighborDesired) -> neighbor.setNeighborPriority(this, neighborsPriority));
+ if (getCurrentPriority() != priority) {
+ this.w.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority
+ int neighborsPriority = getNeighborsPriority();
+ this.neighbors.forEach((neighbor, neighborDesired) -> neighbor.setNeighborPriority(this, neighborsPriority));
+ }
+ // Paper end
this.oldTicketLevel = this.ticketLevel;
// CraftBukkit start
// ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins.
@@ -669,6 +814,7 @@ public class PlayerChunk {
if (!playerchunk_state.isAtLeast(PlayerChunk.State.BORDER) && playerchunk_state1.isAtLeast(PlayerChunk.State.BORDER)) {
- this.getStatusFutureUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+ this.getStatusFutureUncheckedMain(ChunkStatus.FULL).thenAccept((either) -> { // Paper - ensure main
Chunk chunk = (Chunk)either.left().orElse(null);
if (chunk != null) {
playerchunkmap.callbackExecutor.execute(() -> {
@@ -669,6 +819,7 @@ public class PlayerChunk {
public interface c {
@ -523,10 +602,34 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..ee70efd4910fbbc489d4eb41342ece44
}
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
index f1c3cb3ff8961bc688a1d38cd79b999e539cf866..7b4700bdef3a4dc89fd8ba0c98d76c63ad18d5de 100644
index f1c3cb3ff8961bc688a1d38cd79b999e539cf866..8fa1d5f7af6920a5fc63347c3a5dcfc5948d50c4 100644
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
@@ -375,6 +375,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
@@ -50,6 +50,7 @@ import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.entity.Player; // CraftBukkit
+import org.spigotmc.AsyncCatcher;
public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
@@ -123,6 +124,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
@Override
public void execute(Runnable runnable) {
+ AsyncCatcher.catchOp("Callback Executor execute");
if (queued == null) {
queued = new java.util.ArrayDeque<>();
}
@@ -131,6 +133,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
@Override
public void run() {
+ AsyncCatcher.catchOp("Callback Executor run");
if (queued == null) {
return;
}
@@ -375,6 +378,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
(EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> newState) -> {
@ -534,7 +637,7 @@ index f1c3cb3ff8961bc688a1d38cd79b999e539cf866..7b4700bdef3a4dc89fd8ba0c98d76c63
if (newState.size() != 1) {
return;
}
@@ -393,7 +394,8 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
@@ -393,7 +397,8 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
}
ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(rangeX, rangeZ);
PlayerChunkMap.this.world.getChunkProvider().removeTicketAtLevel(TicketType.PLAYER, chunkPos, 31, chunkPos); // entity ticking level, TODO check on update
@ -544,20 +647,28 @@ index f1c3cb3ff8961bc688a1d38cd79b999e539cf866..7b4700bdef3a4dc89fd8ba0c98d76c63
this.playerViewDistanceNoTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
this.playerViewDistanceBroadcastMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
(EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
@@ -410,6 +412,77 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
@@ -410,6 +415,85 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
});
// Paper end - no-tick view distance
}
+ // Paper start - Chunk Prioritization
+ private static final int[][] neighborMatrix = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}};
+ public void queueHolderUpdate(PlayerChunk playerchunk) {
+ executor.execute(() -> {
+ if (isUnloading(playerchunk)) return; // unloaded
+ Runnable runnable = () -> {
+ if (isUnloading(playerchunk)) {
+ return; // unloaded
+ }
+ chunkDistanceManager.pendingChunkUpdates.add(playerchunk);
+ if (!chunkDistanceManager.pollingPendingChunkUpdates) {
+ world.getChunkProvider().tickDistanceManager();
+ }
+ });
+ };
+ if (MCUtil.isMainThread()) {
+ // We can't use executor here because it will not execute tasks if its currently in the middle of executing tasks...
+ runnable.run();
+ } else {
+ executor.execute(runnable);
+ }
+ }
+
+ public boolean isUnloading(PlayerChunk playerchunk) {
@ -622,7 +733,7 @@ index f1c3cb3ff8961bc688a1d38cd79b999e539cf866..7b4700bdef3a4dc89fd8ba0c98d76c63
public void updatePlayerMobTypeMap(Entity entity) {
if (!this.world.paperConfig.perPlayerMobSpawns) {
@@ -539,6 +612,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
@@ -539,6 +623,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
List<CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>>> list = Lists.newArrayList();
int j = chunkcoordintpair.x;
int k = chunkcoordintpair.z;
@ -630,7 +741,7 @@ index f1c3cb3ff8961bc688a1d38cd79b999e539cf866..7b4700bdef3a4dc89fd8ba0c98d76c63
for (int l = -i; l <= i; ++l) {
for (int i1 = -i; i1 <= i; ++i1) {
@@ -557,6 +631,14 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
@@ -557,6 +642,14 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
ChunkStatus chunkstatus = (ChunkStatus) intfunction.apply(j1);
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = playerchunk.a(chunkstatus, this);
@ -645,7 +756,7 @@ index f1c3cb3ff8961bc688a1d38cd79b999e539cf866..7b4700bdef3a4dc89fd8ba0c98d76c63
list.add(completablefuture);
}
@@ -1020,14 +1102,22 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
@@ -1020,14 +1113,22 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
};
CompletableFuture<NBTTagCompound> chunkSaveFuture = this.world.asyncChunkTaskManager.getChunkSaveFuture(chunkcoordintpair.x, chunkcoordintpair.z);
@ -673,7 +784,7 @@ index f1c3cb3ff8961bc688a1d38cd79b999e539cf866..7b4700bdef3a4dc89fd8ba0c98d76c63
return ret;
// Paper end
}
@@ -1156,7 +1246,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
@@ -1156,7 +1257,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
long i = playerchunk.i().pair();
playerchunk.getClass();