diff --git a/Spigot-Server-Patches/0375-ChunkMapDistance-CME.patch b/Spigot-Server-Patches/0375-ChunkMapDistance-CME.patch index 02d7d64ba..02f67c502 100644 --- a/Spigot-Server-Patches/0375-ChunkMapDistance-CME.patch +++ b/Spigot-Server-Patches/0375-ChunkMapDistance-CME.patch @@ -5,19 +5,28 @@ Subject: [PATCH] ChunkMapDistance CME diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java -index ae661297774f51c8b61fc08ca43e13ba368d0021..682a64c775adc1254f12d9f93b23375735ed4895 100644 +index ae661297774f51c8b61fc08ca43e13ba368d0021..83da76fdc495225b563cecbdb71422aec2b534f3 100644 --- a/src/main/java/net/minecraft/server/ChunkMapDistance.java +++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java -@@ -33,7 +33,7 @@ public abstract class ChunkMapDistance { +@@ -33,7 +33,16 @@ public abstract class ChunkMapDistance { private final ChunkMapDistance.a e = new ChunkMapDistance.a(); private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); private final ChunkMapDistance.c g = new ChunkMapDistance.c(33); - private final Set pendingChunkUpdates = Sets.newHashSet(); -+ private final java.util.Queue pendingChunkUpdates = new java.util.ArrayDeque<>(); // PAIL pendingChunkUpdates // Paper - use a queue ++ // Paper start use a queue, but still keep unique requirement ++ public final java.util.Queue pendingChunkUpdates = new java.util.ArrayDeque() { ++ @Override ++ public boolean add(PlayerChunk o) { ++ if (o.isUpdateQueued) return true; ++ o.isUpdateQueued = true; ++ return super.add(o); ++ } ++ }; ++ // Paper end private final ChunkTaskQueueSorter i; private final Mailbox> j; private final Mailbox k; -@@ -94,26 +94,12 @@ public abstract class ChunkMapDistance { +@@ -94,26 +103,14 @@ public abstract class ChunkMapDistance { ; } @@ -43,9 +52,23 @@ index ae661297774f51c8b61fc08ca43e13ba368d0021..682a64c775adc1254f12d9f93b233757 - // CraftBukkit end - + while(!this.pendingChunkUpdates.isEmpty()) { -+ this.pendingChunkUpdates.remove().a(playerchunkmap); ++ PlayerChunk remove = this.pendingChunkUpdates.remove(); ++ remove.isUpdateQueued = false; ++ remove.a(playerchunkmap); + } + // Paper end return true; } else { if (!this.l.isEmpty()) { +diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java +index 6e9f402fb0faccc222b4289deb36e2d85a66eb7c..980044b9a81232e7d0eab8e4947db6ca1f845c1c 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunk.java ++++ b/src/main/java/net/minecraft/server/PlayerChunk.java +@@ -18,6 +18,7 @@ public class PlayerChunk { + private static final CompletableFuture> UNLOADED_CHUNK_FUTURE = CompletableFuture.completedFuture(PlayerChunk.UNLOADED_CHUNK); + private static final List CHUNK_STATUSES = ChunkStatus.a(); + private static final PlayerChunk.State[] CHUNK_STATES = PlayerChunk.State.values(); ++ boolean isUpdateQueued = false; // Paper + private final AtomicReferenceArray>> statusFutures; + private volatile CompletableFuture> fullChunkFuture; private int fullChunkCreateCount; private volatile boolean isFullChunkReady; // Paper - cache chunk ticking stage + private volatile CompletableFuture> tickingFuture; private volatile boolean isTickingReady; // Paper - cache chunk ticking stage diff --git a/Spigot-Server-Patches/0377-Chunk-debug-command.patch b/Spigot-Server-Patches/0377-Chunk-debug-command.patch index 0b47d2e3f..8ec744d77 100644 --- a/Spigot-Server-Patches/0377-Chunk-debug-command.patch +++ b/Spigot-Server-Patches/0377-Chunk-debug-command.patch @@ -399,10 +399,10 @@ index 206d04dcce1d7d074cf7151a083bdc626b0b8e07..f75f48a3d0b0bc1da3c5ae3b3cf20b64 + } } diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 6e9f402fb0faccc222b4289deb36e2d85a66eb7c..a640cb3845a853780b8cc2dbfc6e9be3728817e7 100644 +index 980044b9a81232e7d0eab8e4947db6ca1f845c1c..47e3e618c9e683e6975fb64e1094dc7078574dae 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -26,7 +26,7 @@ public class PlayerChunk { +@@ -27,7 +27,7 @@ public class PlayerChunk { public int oldTicketLevel; private int ticketLevel; private int n; diff --git a/Spigot-Server-Patches/0378-incremental-chunk-saving.patch b/Spigot-Server-Patches/0378-incremental-chunk-saving.patch index 5c1a01b75..348a76a17 100644 --- a/Spigot-Server-Patches/0378-incremental-chunk-saving.patch +++ b/Spigot-Server-Patches/0378-incremental-chunk-saving.patch @@ -108,10 +108,10 @@ index 8b499c815c77bf5b356d4216ba6cbf2a329c9aca..cfed5f51431ec5aecb538a321327bfb6 this.methodProfiler.enter("snooper"); if (((DedicatedServer) this).getDedicatedServerProperties().snooperEnabled && !this.snooper.d() && this.ticks > 100) { // Spigot diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index a640cb3845a853780b8cc2dbfc6e9be3728817e7..3d255b19647fb37f53a420c907bc634181580c18 100644 +index 47e3e618c9e683e6975fb64e1094dc7078574dae..ed9ada49c7cc1131691bd6e005b2380274ef23e3 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -40,6 +40,9 @@ public class PlayerChunk { +@@ -41,6 +41,9 @@ public class PlayerChunk { private final PlayerChunkMap chunkMap; // Paper @@ -121,7 +121,7 @@ index a640cb3845a853780b8cc2dbfc6e9be3728817e7..3d255b19647fb37f53a420c907bc6341 public PlayerChunk(ChunkCoordIntPair chunkcoordintpair, int i, LightEngine lightengine, PlayerChunk.c playerchunk_c, PlayerChunk.d playerchunk_d) { this.statusFutures = new AtomicReferenceArray(PlayerChunk.CHUNK_STATUSES.size()); this.fullChunkFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE; -@@ -385,7 +388,19 @@ public class PlayerChunk { +@@ -386,7 +389,19 @@ public class PlayerChunk { boolean flag2 = playerchunk_state.isAtLeast(PlayerChunk.State.BORDER); boolean flag3 = playerchunk_state1.isAtLeast(PlayerChunk.State.BORDER); @@ -141,7 +141,7 @@ index a640cb3845a853780b8cc2dbfc6e9be3728817e7..3d255b19647fb37f53a420c907bc6341 if (!flag2 && flag3) { // Paper start - cache ticking ready status int expectCreateCount = ++this.fullChunkCreateCount; -@@ -505,8 +520,32 @@ public class PlayerChunk { +@@ -506,8 +521,32 @@ public class PlayerChunk { } public void m() { diff --git a/Spigot-Server-Patches/0380-Fix-World-isChunkGenerated-calls.patch b/Spigot-Server-Patches/0380-Fix-World-isChunkGenerated-calls.patch index 744b0afbf..8edc363ea 100644 --- a/Spigot-Server-Patches/0380-Fix-World-isChunkGenerated-calls.patch +++ b/Spigot-Server-Patches/0380-Fix-World-isChunkGenerated-calls.patch @@ -108,10 +108,10 @@ index f0a052eec2fb72b11dc70bf62a5e57f599bbc190..2f95174fcc467908808ed3f2dc956bdc @Nullable private PersistentStructureLegacy c; diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 3d255b19647fb37f53a420c907bc634181580c18..040d4b41ea2223937ca22de2d40560f97b310f9a 100644 +index ed9ada49c7cc1131691bd6e005b2380274ef23e3..52ea4f05a0c7f29f62f31bb032a5ceb905107e60 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -114,6 +114,19 @@ public class PlayerChunk { +@@ -115,6 +115,19 @@ public class PlayerChunk { Either either = (Either) statusFuture.getNow(null); return either == null ? null : (Chunk) either.left().orElse(null); } diff --git a/Spigot-Server-Patches/0390-Asynchronous-chunk-IO-and-loading.patch b/Spigot-Server-Patches/0390-Asynchronous-chunk-IO-and-loading.patch index 585330c1a..74853d4bd 100644 --- a/Spigot-Server-Patches/0390-Asynchronous-chunk-IO-and-loading.patch +++ b/Spigot-Server-Patches/0390-Asynchronous-chunk-IO-and-loading.patch @@ -160,6 +160,44 @@ index fa1c920ea6092259149f9e7f9cd7cc1ed27bf338..98acbfa44dd9042b26fdf719d7748f92 } public static Timing getTickList(WorldServer worldserver, String timingsType) { +diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java +index af810987846efcd2bffbd23c31481b2d31c168dd..331493a172f58e71b464d635efdba461082bd27d 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java ++++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java +@@ -1,5 +1,6 @@ + package com.destroystokyo.paper; + ++import com.destroystokyo.paper.io.chunk.ChunkTaskManager; + import com.google.common.base.Functions; + import com.google.common.collect.Iterables; + import com.google.common.collect.Lists; +@@ -28,14 +29,14 @@ public class PaperCommand extends Command { + public PaperCommand(String name) { + super(name); + this.description = "Paper related commands"; +- this.usageMessage = "/paper [heap | entity | reload | version | debug | chunkinfo]"; ++ this.usageMessage = "/paper [heap | entity | reload | version | debug | dumpwaiting | chunkinfo]"; + this.setPermission("bukkit.command.paper"); + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { + if (args.length <= 1) +- return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "chunkinfo"); ++ return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "dumpwaiting", "chunkinfo"); + + switch (args[0].toLowerCase(Locale.ENGLISH)) + { +@@ -127,6 +128,9 @@ public class PaperCommand extends Command { + case "debug": + doDebug(sender, args); + break; ++ case "dumpwaiting": ++ ChunkTaskManager.dumpAllChunkLoadInfo(); ++ break; + case "chunkinfo": + doChunkInfo(sender, args); + break; diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java index dbd14399707cdd43f98af40191be8ff3e76edf43..74295466e53db06d0d019a13768f3575ac61d699 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -1809,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..2b20c159f6bb425be70201cf33159aa9e949eb54 +index 0000000000000000000000000000000000000000..d9580eb998801edd34c610ced3f82f9627c6685b --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java -@@ -0,0 +1,492 @@ +@@ -0,0 +1,502 @@ +package com.destroystokyo.paper.io.chunk; + +import com.destroystokyo.paper.io.PaperFileIOThread; @@ -1824,7 +1862,9 @@ index 0000000000000000000000000000000000000000..2b20c159f6bb425be70201cf33159aa9 +import net.minecraft.server.IChunkAccess; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.NBTTagCompound; ++import net.minecraft.server.PlayerChunk; +import net.minecraft.server.WorldServer; ++import org.apache.commons.lang.StringUtils; +import org.apache.logging.log4j.Level; +import org.bukkit.Bukkit; +import org.spigotmc.AsyncCatcher; @@ -1912,20 +1952,28 @@ index 0000000000000000000000000000000000000000..2b20c159f6bb425be70201cf33159aa9 + // 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); + -+ if (chunkHolder == null) { -+ PaperFileIOThread.LOGGER.log(Level.ERROR, "Chunk Holder - null"); -+ } else { -+ IChunkAccess chunk = chunkHolder.getAvailableChunkNow(); -+ net.minecraft.server.ChunkStatus holderStatus = chunkHolder.getChunkHolderStatus(); -+ PaperFileIOThread.LOGGER.log(Level.ERROR, "Chunk Holder - non-null"); -+ PaperFileIOThread.LOGGER.log(Level.ERROR, "Chunk Status - " + ((chunk == null) ? "null chunk" : chunk.getChunkStatus().toString())); -+ PaperFileIOThread.LOGGER.log(Level.ERROR, "Chunk Holder Status - " + ((holderStatus == null) ? "null" : holderStatus.toString())); -+ } -+ ++ dumpChunkInfo(chunkHolder, chunkInfo.chunkX, chunkInfo.chunkZ); + } + } + } + ++ static void dumpChunkInfo(PlayerChunk chunkHolder, int x, int z) { ++ dumpChunkInfo(chunkHolder, x, z, 0); ++ } ++ static void dumpChunkInfo(PlayerChunk chunkHolder, int x, int z, int indent) { ++ String indentStr = StringUtils.repeat(" ", indent); ++ if (chunkHolder == null) { ++ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Holder - null for (" + x +"," + z +")"); ++ } else { ++ IChunkAccess chunk = chunkHolder.getAvailableChunkNow(); ++ net.minecraft.server.ChunkStatus holderStatus = chunkHolder.getChunkHolderStatus(); ++ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Holder - non-null"); ++ 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())); ++ } ++ } ++ + public static void initGlobalLoadThreads(int threads) { + if (threads <= 0 || globalWorkers != null) { + return; @@ -3004,10 +3052,10 @@ index 4c52c57c02571353f71772e3650932f314da62ca..71daa0cb08d69c16bded510d1a490534 @Override diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 040d4b41ea2223937ca22de2d40560f97b310f9a..bf592125f4ce5d7ea6e802e637ee3bfbe25d23aa 100644 +index 52ea4f05a0c7f29f62f31bb032a5ceb905107e60..0f1576effe10795bcb8ed3b519f4dbafdf9ea6ed 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -127,6 +127,18 @@ public class PlayerChunk { +@@ -128,6 +128,18 @@ public class PlayerChunk { } return null; } @@ -3026,7 +3074,7 @@ index 040d4b41ea2223937ca22de2d40560f97b310f9a..bf592125f4ce5d7ea6e802e637ee3bfb // Paper end public CompletableFuture> getStatusFutureUnchecked(ChunkStatus chunkstatus) { -@@ -352,7 +364,7 @@ public class PlayerChunk { +@@ -353,7 +365,7 @@ public class PlayerChunk { ChunkStatus chunkstatus = getChunkStatus(this.oldTicketLevel); ChunkStatus chunkstatus1 = getChunkStatus(this.ticketLevel); boolean flag = this.oldTicketLevel <= PlayerChunkMap.GOLDEN_TICKET; @@ -3035,7 +3083,7 @@ index 040d4b41ea2223937ca22de2d40560f97b310f9a..bf592125f4ce5d7ea6e802e637ee3bfb PlayerChunk.State playerchunk_state = getChunkState(this.oldTicketLevel); PlayerChunk.State playerchunk_state1 = getChunkState(this.ticketLevel); // CraftBukkit start -@@ -388,6 +400,12 @@ public class PlayerChunk { +@@ -389,6 +401,12 @@ public class PlayerChunk { } }); diff --git a/Spigot-Server-Patches/0392-Reduce-sync-loads.patch b/Spigot-Server-Patches/0392-Reduce-sync-loads.patch index 5c0e826bf..5bcaddb38 100644 --- a/Spigot-Server-Patches/0392-Reduce-sync-loads.patch +++ b/Spigot-Server-Patches/0392-Reduce-sync-loads.patch @@ -11,12 +11,13 @@ it must be enabled by setting the startup flag -Dpaper.debug-sync-loads=true To get a debug log for sync loads, the command is /paper syncloadinfo diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java -index af810987846efcd2bffbd23c31481b2d31c168dd..ddb60e9a48e5e7225ad575240b94fda24b6b78ca 100644 +index 331493a172f58e71b464d635efdba461082bd27d..182b440ba4802d199b8e44f7779b3401ace495d5 100644 --- a/src/main/java/com/destroystokyo/paper/PaperCommand.java +++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java -@@ -1,9 +1,13 @@ +@@ -1,10 +1,14 @@ package com.destroystokyo.paper; + import com.destroystokyo.paper.io.chunk.ChunkTaskManager; +import com.destroystokyo.paper.io.SyncLoadFinder; import com.google.common.base.Functions; import com.google.common.collect.Iterables; @@ -28,7 +29,7 @@ index af810987846efcd2bffbd23c31481b2d31c168dd..ddb60e9a48e5e7225ad575240b94fda2 import net.minecraft.server.*; import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.Pair; -@@ -18,6 +22,9 @@ import org.bukkit.craftbukkit.CraftWorld; +@@ -19,6 +23,9 @@ import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.entity.Player; import java.io.File; @@ -38,24 +39,24 @@ index af810987846efcd2bffbd23c31481b2d31c168dd..ddb60e9a48e5e7225ad575240b94fda2 import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; -@@ -28,14 +35,14 @@ public class PaperCommand extends Command { +@@ -29,14 +36,14 @@ public class PaperCommand extends Command { public PaperCommand(String name) { super(name); this.description = "Paper related commands"; -- this.usageMessage = "/paper [heap | entity | reload | version | debug | chunkinfo]"; -+ this.usageMessage = "/paper [heap | entity | reload | version | debug | chunkinfo | syncloadinfo]"; +- this.usageMessage = "/paper [heap | entity | reload | version | debug | dumpwaiting | chunkinfo]"; ++ this.usageMessage = "/paper [heap | entity | reload | version | debug | dumpwaiting | chunkinfo | syncloadinfo]"; this.setPermission("bukkit.command.paper"); } @Override public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { if (args.length <= 1) -- return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "chunkinfo"); -+ return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "chunkinfo", "syncloadinfo"); +- return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "dumpwaiting", "chunkinfo"); ++ return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "dumpwaiting", "chunkinfo", "syncloadinfo"); switch (args[0].toLowerCase(Locale.ENGLISH)) { -@@ -130,6 +137,9 @@ public class PaperCommand extends Command { +@@ -134,6 +141,9 @@ public class PaperCommand extends Command { case "chunkinfo": doChunkInfo(sender, args); break; @@ -65,7 +66,7 @@ index af810987846efcd2bffbd23c31481b2d31c168dd..ddb60e9a48e5e7225ad575240b94fda2 case "ver": case "version": Command ver = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version"); -@@ -146,6 +156,40 @@ public class PaperCommand extends Command { +@@ -150,6 +160,40 @@ public class PaperCommand extends Command { return true; } diff --git a/Spigot-Server-Patches/0446-Optimise-TickListServer-by-rewriting-it.patch b/Spigot-Server-Patches/0446-Optimise-TickListServer-by-rewriting-it.patch index 534c7eeb4..ab0f90ecc 100644 --- a/Spigot-Server-Patches/0446-Optimise-TickListServer-by-rewriting-it.patch +++ b/Spigot-Server-Patches/0446-Optimise-TickListServer-by-rewriting-it.patch @@ -42,7 +42,7 @@ sets the excessive tick delay to the specified ticks (defaults to 60 * 20 ticks, aka 60 seconds) diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index f4836e2da1061e7aa62ddb01c8ca7b3467b18415..647f6fc8efb350fbd0bc4c40358a998f8b89b96a 100644 +index 74295466e53db06d0d019a13768f3575ac61d699..f1b41e16c8ce8323a896339c5d822f8ff7d8f7e6 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -369,6 +369,13 @@ public class PaperConfig { @@ -883,7 +883,7 @@ index 0000000000000000000000000000000000000000..118988c39e58f28e8a2851792b9c014f + } +} diff --git a/src/main/java/net/minecraft/server/BlockPosition.java b/src/main/java/net/minecraft/server/BlockPosition.java -index 063f8eb08635aaa44803f2a67d118805294ae938..58ba1209155e05e802171800f4420e3ae419950f 100644 +index 3fcfe416d26808fa1c9bfdc5b413b149764c544a..3bf17ccdaef21322b787db538d569e0bc614ef22 100644 --- a/src/main/java/net/minecraft/server/BlockPosition.java +++ b/src/main/java/net/minecraft/server/BlockPosition.java @@ -126,6 +126,7 @@ public class BlockPosition extends BaseBlockPosition implements MinecraftSeriali @@ -895,7 +895,7 @@ index 063f8eb08635aaa44803f2a67d118805294ae938..58ba1209155e05e802171800f4420e3a return this.b(baseblockposition.getX(), baseblockposition.getY(), baseblockposition.getZ()); } diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index e67e00653575c3e57fe16980d2ff074d8413a95e..ca1b5b3b094b847f96742d8466fc42c5bdedbff5 100644 +index 4f65c3aca4e1c299114c03339605e0749a969653..fd998e4fb1534690a2ef8c1bca55e0ae9fe855f9 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -200,6 +200,13 @@ public class ChunkProviderServer extends IChunkProvider { @@ -984,10 +984,10 @@ index 33cfeabdee03195a294f303f28044a313cb1c4ed..2287e47d1b891135a5f2579ec324c705 public String toString() { return this.e + ": " + this.a + ", " + this.b + ", " + this.c + ", " + this.f; diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index bf592125f4ce5d7ea6e802e637ee3bfbe25d23aa..3d610e41969768da0d2848fa1ae195035ccfd660 100644 +index 0f1576effe10795bcb8ed3b519f4dbafdf9ea6ed..afc92dd031cdaf725b85c0b301d5a5a21da54720 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -472,7 +472,9 @@ public class PlayerChunk { +@@ -473,7 +473,9 @@ public class PlayerChunk { PlayerChunk.this.isTickingReady = true; @@ -1161,7 +1161,7 @@ index f533860bbed19ff2915c90186c259b466f41ce90..3f1aa5ced697490b5481ba992cf5af5d } } diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index fcbc9f29139ce5cd3b165130006f9c326a5d9eea..5173731dc55db416a312e13afd09e4e69d829e45 100644 +index 5519fb529dd8e1f93aab79dbc434715e08edb94f..be61f898cc96afeffe6ff2eb0ae3c4ce8da10b80 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -170,6 +170,15 @@ public class WorldServer extends World { diff --git a/Spigot-Server-Patches/0467-Improved-Watchdog-Support.patch b/Spigot-Server-Patches/0467-Improved-Watchdog-Support.patch index 73f936d9b..05bfedbd0 100644 --- a/Spigot-Server-Patches/0467-Improved-Watchdog-Support.patch +++ b/Spigot-Server-Patches/0467-Improved-Watchdog-Support.patch @@ -313,10 +313,18 @@ index aefea3a9a8b9b75c62bd20018be7cd166a213001..123de5ac9026508e21cdc225f0962f5c String[] split = restartScript.split( " " ); if ( split.length > 0 && new File( split[0] ).isFile() ) diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java -index 5bdcdcf9e85b73086722783bff26321d03382bb9..fe4b8caf28b2d36b2034ac90b1a76dea7b691feb 100644 +index 5bdcdcf9e85b73086722783bff26321d03382bb9..513c1041c34ebb3ac1775674a3f4526693759c08 100644 --- a/src/main/java/org/spigotmc/WatchdogThread.java +++ b/src/main/java/org/spigotmc/WatchdogThread.java -@@ -41,6 +41,7 @@ public class WatchdogThread extends Thread +@@ -13,6 +13,7 @@ import org.bukkit.Bukkit; + public class WatchdogThread extends Thread + { + ++ public static final boolean DISABLE_WATCHDOG = Boolean.getBoolean("disable.watchdog"); // Paper + private static WatchdogThread instance; + private final long timeoutTime; + private final long earlyWarningEvery; // Paper - Timeout time for just printing a dump but not restarting +@@ -41,6 +42,7 @@ public class WatchdogThread extends Thread { if ( instance == null ) { @@ -324,13 +332,13 @@ index 5bdcdcf9e85b73086722783bff26321d03382bb9..fe4b8caf28b2d36b2034ac90b1a76dea instance = new WatchdogThread( timeoutTime * 1000L, restart ); instance.start(); } -@@ -67,12 +68,13 @@ public class WatchdogThread extends Thread +@@ -67,12 +69,13 @@ public class WatchdogThread extends Thread // Paper start Logger log = Bukkit.getServer().getLogger(); long currentTime = monotonicMillis(); - if ( lastTick != 0 && currentTime > lastTick + earlyWarningEvery && !Boolean.getBoolean("disable.watchdog") ) + MinecraftServer server = MinecraftServer.getServer(); -+ if (lastTick != 0 && hasStarted && (!server.isRunning() || (currentTime > lastTick + earlyWarningEvery && !Boolean.getBoolean("disable.watchdog")) )) ++ if (lastTick != 0 && hasStarted && (!server.isRunning() || (currentTime > lastTick + earlyWarningEvery && !DISABLE_WATCHDOG) )) { - boolean isLongTimeout = currentTime > lastTick + timeoutTime; + boolean isLongTimeout = currentTime > lastTick + timeoutTime || (!server.isRunning() && !server.hasStopped() && currentTime > lastTick + 1000); @@ -341,7 +349,7 @@ index 5bdcdcf9e85b73086722783bff26321d03382bb9..fe4b8caf28b2d36b2034ac90b1a76dea lastEarlyWarning = currentTime; if (isLongTimeout) { // Paper end -@@ -114,7 +116,7 @@ public class WatchdogThread extends Thread +@@ -114,7 +117,7 @@ public class WatchdogThread extends Thread log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper @@ -350,7 +358,7 @@ index 5bdcdcf9e85b73086722783bff26321d03382bb9..fe4b8caf28b2d36b2034ac90b1a76dea log.log( Level.SEVERE, "------------------------------" ); // // Paper start - Only print full dump on long timeouts -@@ -135,9 +137,24 @@ public class WatchdogThread extends Thread +@@ -135,9 +138,24 @@ public class WatchdogThread extends Thread if ( isLongTimeout ) { diff --git a/Spigot-Server-Patches/0482-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch b/Spigot-Server-Patches/0482-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch index 6303099e5..68769c39f 100644 --- a/Spigot-Server-Patches/0482-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch +++ b/Spigot-Server-Patches/0482-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch @@ -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/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java -index 682a64c775adc1254f12d9f93b23375735ed4895..534bb87caf88f0f1bf7988494274b762ab7210e1 100644 +index 83da76fdc495225b563cecbdb71422aec2b534f3..4e0ea454f00c69f03023f01c1d4bd2eda5553a02 100644 --- a/src/main/java/net/minecraft/server/ChunkMapDistance.java +++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java -@@ -226,6 +226,7 @@ public abstract class ChunkMapDistance { +@@ -237,6 +237,7 @@ public abstract class ChunkMapDistance { public void b(SectionPosition sectionposition, EntityPlayer entityplayer) { long i = sectionposition.u().pair(); ObjectSet objectset = (ObjectSet) this.c.get(i); diff --git a/Spigot-Server-Patches/0503-Optimize-isOutsideRange-to-use-distance-maps.patch b/Spigot-Server-Patches/0503-Optimize-isOutsideRange-to-use-distance-maps.patch index 9dc11e6e5..1cfd77b74 100644 --- a/Spigot-Server-Patches/0503-Optimize-isOutsideRange-to-use-distance-maps.patch +++ b/Spigot-Server-Patches/0503-Optimize-isOutsideRange-to-use-distance-maps.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Optimize isOutsideRange to use distance maps Use a distance map to find the players in range quickly diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java -index 534bb87caf88f0f1bf7988494274b762ab7210e1..b98abed74f214932b4a226f12645f987191b0097 100644 +index 4e0ea454f00c69f03023f01c1d4bd2eda5553a02..353b186060b2c0417a49ab3865ea5972c859b016 100644 --- a/src/main/java/net/minecraft/server/ChunkMapDistance.java +++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java @@ -31,7 +31,7 @@ public abstract class ChunkMapDistance { @@ -16,9 +16,9 @@ index 534bb87caf88f0f1bf7988494274b762ab7210e1..b98abed74f214932b4a226f12645f987 - private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); + public static final int MOB_SPAWN_RANGE = 8; //private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); // Paper - no longer used private final ChunkMapDistance.c g = new ChunkMapDistance.c(33); - private final java.util.Queue pendingChunkUpdates = new java.util.ArrayDeque<>(); // PAIL pendingChunkUpdates // Paper - use a queue - private final ChunkTaskQueueSorter i; -@@ -41,6 +41,8 @@ public abstract class ChunkMapDistance { + // Paper start use a queue, but still keep unique requirement + public final java.util.Queue pendingChunkUpdates = new java.util.ArrayDeque() { +@@ -50,6 +50,8 @@ public abstract class ChunkMapDistance { private final Executor m; private long currentTick; @@ -27,7 +27,7 @@ index 534bb87caf88f0f1bf7988494274b762ab7210e1..b98abed74f214932b4a226f12645f987 protected ChunkMapDistance(Executor executor, Executor executor1) { executor1.getClass(); Mailbox mailbox = Mailbox.a("player ticket throttler", executor1::execute); -@@ -85,7 +87,7 @@ public abstract class ChunkMapDistance { +@@ -94,7 +96,7 @@ public abstract class ChunkMapDistance { protected abstract PlayerChunk a(long i, int j, @Nullable PlayerChunk playerchunk, int k); public boolean a(PlayerChunkMap playerchunkmap) { @@ -36,7 +36,7 @@ index 534bb87caf88f0f1bf7988494274b762ab7210e1..b98abed74f214932b4a226f12645f987 this.g.a(); int i = Integer.MAX_VALUE - this.e.a(Integer.MAX_VALUE); boolean flag = i != 0; -@@ -219,7 +221,7 @@ public abstract class ChunkMapDistance { +@@ -230,7 +232,7 @@ public abstract class ChunkMapDistance { ((ObjectSet) this.c.computeIfAbsent(i, (j) -> { return new ObjectOpenHashSet(); })).add(entityplayer); @@ -45,7 +45,7 @@ index 534bb87caf88f0f1bf7988494274b762ab7210e1..b98abed74f214932b4a226f12645f987 this.g.b(i, 0, true); } -@@ -231,7 +233,7 @@ public abstract class ChunkMapDistance { +@@ -242,7 +244,7 @@ public abstract class ChunkMapDistance { objectset.remove(entityplayer); if (objectset.isEmpty()) { this.c.remove(i); @@ -54,7 +54,7 @@ index 534bb87caf88f0f1bf7988494274b762ab7210e1..b98abed74f214932b4a226f12645f987 this.g.b(i, Integer.MAX_VALUE, false); } -@@ -255,13 +257,17 @@ public abstract class ChunkMapDistance { +@@ -266,13 +268,17 @@ public abstract class ChunkMapDistance { } public int b() { @@ -161,10 +161,10 @@ index 79c2187b7383336e7574709e6d4ad805e557976f..0560eca744cb2032bb6a3faf5aeafa95 super((World) worldserver, gameprofile); playerinteractmanager.player = this; diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 3d610e41969768da0d2848fa1ae195035ccfd660..c4aad20a2952cc34e334ba665a6e0910d5609497 100644 +index afc92dd031cdaf725b85c0b301d5a5a21da54720..6980d19f36c18cdbed6679dbdf04afd694e078b6 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -43,6 +43,18 @@ public class PlayerChunk { +@@ -44,6 +44,18 @@ public class PlayerChunk { long lastAutoSaveTime; // Paper - incremental autosave long inactiveTimeStart; // Paper - incremental autosave @@ -183,7 +183,7 @@ index 3d610e41969768da0d2848fa1ae195035ccfd660..c4aad20a2952cc34e334ba665a6e0910 public PlayerChunk(ChunkCoordIntPair chunkcoordintpair, int i, LightEngine lightengine, PlayerChunk.c playerchunk_c, PlayerChunk.d playerchunk_d) { this.statusFutures = new AtomicReferenceArray(PlayerChunk.CHUNK_STATUSES.size()); this.fullChunkFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE; -@@ -59,6 +71,7 @@ public class PlayerChunk { +@@ -60,6 +72,7 @@ public class PlayerChunk { this.n = this.oldTicketLevel; this.a(i); this.chunkMap = (PlayerChunkMap)playerchunk_d; // Paper diff --git a/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch b/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch index cf9b8030b..cd0f5fb2b 100644 --- a/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch +++ b/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch @@ -79,10 +79,10 @@ index 750fb07756f7e40b21f8ab0925f2e842aae50f7b..8c1f3290d23795b58a30274c9437dc7d public final boolean areNeighboursLoaded(final int radius) { diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java -index b98abed74f214932b4a226f12645f987191b0097..716d4eab382244ee9bc4855bf0f026e65ec057d6 100644 +index 353b186060b2c0417a49ab3865ea5972c859b016..586a20fe5c77c2ad5fa26f337a94a16e21d8b5e2 100644 --- a/src/main/java/net/minecraft/server/ChunkMapDistance.java +++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java -@@ -252,7 +252,7 @@ public abstract class ChunkMapDistance { +@@ -263,7 +263,7 @@ public abstract class ChunkMapDistance { return s; } @@ -91,7 +91,7 @@ index b98abed74f214932b4a226f12645f987191b0097..716d4eab382244ee9bc4855bf0f026e6 this.g.a(i); } -@@ -371,7 +371,7 @@ public abstract class ChunkMapDistance { +@@ -382,7 +382,7 @@ public abstract class ChunkMapDistance { private void a(long i, int j, boolean flag, boolean flag1) { if (flag != flag1) { @@ -114,20 +114,10 @@ index 0560eca744cb2032bb6a3faf5aeafa95a7a6815e..07a6fc3d88e7d44bfab7f3d6a0eef7dc super((World) worldserver, gameprofile); playerinteractmanager.player = this; diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index c4aad20a2952cc34e334ba665a6e0910d5609497..845e5d2a8ee025ac61cf916de04e0797e32db568 100644 +index 6980d19f36c18cdbed6679dbdf04afd694e078b6..03fb688fe4bdc19b4bc36b1f1d5b40c61e7bef9b 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -47,14 +47,28 @@ public class PlayerChunk { - // cached here to avoid a map lookup - com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet playersInMobSpawnRange; - com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet playersInChunkTickRange; -+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet playersInTickingRange; - - void updateRanges() { - long key = net.minecraft.server.MCUtil.getCoordinateKey(this.location); - this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key); - this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key); -+ this.playersInTickingRange = this.chunkMap.playerViewDistanceTickMap.getObjectsInRange(key); +@@ -56,6 +56,18 @@ public class PlayerChunk { } // Paper end - optimise isOutsideOfRange @@ -146,7 +136,7 @@ index c4aad20a2952cc34e334ba665a6e0910d5609497..845e5d2a8ee025ac61cf916de04e0797 public PlayerChunk(ChunkCoordIntPair chunkcoordintpair, int i, LightEngine lightengine, PlayerChunk.c playerchunk_c, PlayerChunk.d playerchunk_d) { this.statusFutures = new AtomicReferenceArray(PlayerChunk.CHUNK_STATUSES.size()); this.fullChunkFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE; -@@ -210,7 +224,7 @@ public class PlayerChunk { +@@ -211,7 +223,7 @@ public class PlayerChunk { } public void a(int i, int j, int k) { @@ -155,7 +145,7 @@ index c4aad20a2952cc34e334ba665a6e0910d5609497..845e5d2a8ee025ac61cf916de04e0797 if (chunk != null) { this.r |= 1 << (j >> 4); -@@ -230,7 +244,7 @@ public class PlayerChunk { +@@ -231,7 +243,7 @@ public class PlayerChunk { } public void a(EnumSkyBlock enumskyblock, int i) { @@ -164,7 +154,7 @@ index c4aad20a2952cc34e334ba665a6e0910d5609497..845e5d2a8ee025ac61cf916de04e0797 if (chunk != null) { chunk.setNeedsSaving(true); -@@ -315,9 +329,48 @@ public class PlayerChunk { +@@ -316,9 +328,48 @@ public class PlayerChunk { } private void a(Packet packet, boolean flag) { diff --git a/Spigot-Server-Patches/0507-Fix-Light-Command.patch b/Spigot-Server-Patches/0507-Fix-Light-Command.patch index 87101d0c5..670aa05a6 100644 --- a/Spigot-Server-Patches/0507-Fix-Light-Command.patch +++ b/Spigot-Server-Patches/0507-Fix-Light-Command.patch @@ -7,10 +7,10 @@ This lets you run /paper fixlight (max 5) to automatically fix all light data in the chunks. diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java -index ddb60e9a48e5e7225ad575240b94fda24b6b78ca..beb50f78e8b2f79d45e2e7fdc932e947138be7aa 100644 +index 182b440ba4802d199b8e44f7779b3401ace495d5..10b72083322b7f8e3e14525b3e834f5374ec369d 100644 --- a/src/main/java/com/destroystokyo/paper/PaperCommand.java +++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java -@@ -19,6 +19,7 @@ import org.bukkit.command.Command; +@@ -20,6 +20,7 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftWorld; @@ -18,24 +18,24 @@ index ddb60e9a48e5e7225ad575240b94fda24b6b78ca..beb50f78e8b2f79d45e2e7fdc932e947 import org.bukkit.entity.Player; import java.io.File; -@@ -35,14 +36,14 @@ public class PaperCommand extends Command { +@@ -36,14 +37,14 @@ public class PaperCommand extends Command { public PaperCommand(String name) { super(name); this.description = "Paper related commands"; -- this.usageMessage = "/paper [heap | entity | reload | version | debug | chunkinfo | syncloadinfo]"; -+ this.usageMessage = "/paper [heap | entity | reload | version | debug | chunkinfo | syncloadinfo | fixlight]"; +- this.usageMessage = "/paper [heap | entity | reload | version | debug | dumpwaiting | chunkinfo | syncloadinfo]"; ++ this.usageMessage = "/paper [heap | entity | reload | version | debug | dumpwaiting | chunkinfo | syncloadinfo | fixlight]"; this.setPermission("bukkit.command.paper"); } @Override public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { if (args.length <= 1) -- return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "chunkinfo", "syncloadinfo"); -+ return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "chunkinfo", "syncloadinfo", "fixlight"); +- return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "dumpwaiting", "chunkinfo", "syncloadinfo"); ++ return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "dumpwaiting", "chunkinfo", "syncloadinfo", "fixlight"); switch (args[0].toLowerCase(Locale.ENGLISH)) { -@@ -140,6 +141,9 @@ public class PaperCommand extends Command { +@@ -144,6 +145,9 @@ public class PaperCommand extends Command { case "syncloadinfo": this.doSyncLoadInfo(sender, args); break; @@ -45,7 +45,7 @@ index ddb60e9a48e5e7225ad575240b94fda24b6b78ca..beb50f78e8b2f79d45e2e7fdc932e947 case "ver": case "version": Command ver = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version"); -@@ -156,6 +160,75 @@ public class PaperCommand extends Command { +@@ -160,6 +164,75 @@ public class PaperCommand extends Command { return true; } @@ -122,10 +122,10 @@ index ddb60e9a48e5e7225ad575240b94fda24b6b78ca..beb50f78e8b2f79d45e2e7fdc932e947 if (!SyncLoadFinder.ENABLED) { sender.sendMessage(ChatColor.RED + "This command requires the server startup flag '-Dpaper.debug-sync-loads=true' to be set."); diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 845e5d2a8ee025ac61cf916de04e0797e32db568..9e95c6b54f0855ddde6db6bd3e768e87fee6c21a 100644 +index 03fb688fe4bdc19b4bc36b1f1d5b40c61e7bef9b..aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -328,6 +328,7 @@ public class PlayerChunk { +@@ -327,6 +327,7 @@ public class PlayerChunk { } diff --git a/Spigot-Server-Patches/0529-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch b/Spigot-Server-Patches/0529-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch index 717bb03e6..4037e22c5 100644 --- a/Spigot-Server-Patches/0529-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch +++ b/Spigot-Server-Patches/0529-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch @@ -22,20 +22,68 @@ view distance holds on you. 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 d9580eb998801edd34c610ced3f82f9627c6685b..537a22b72aa2c90f0102b035843e09c54e5ae39f 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; + import com.destroystokyo.paper.io.IOUtil; + import com.destroystokyo.paper.io.PrioritizedTaskQueue; + import com.destroystokyo.paper.io.QueueExecutorThread; ++import it.unimi.dsi.fastutil.longs.Long2ObjectMap; ++import net.minecraft.server.ChunkCoordIntPair; + import net.minecraft.server.ChunkRegionLoader; ++import net.minecraft.server.ChunkStatus; + import net.minecraft.server.IAsyncTaskHandler; + import net.minecraft.server.IChunkAccess; + import net.minecraft.server.MinecraftServer; +@@ -118,6 +121,32 @@ 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 entry : chunkHolder.neighborPriorities.long2ObjectEntrySet()) { ++ ChunkCoordIntPair r = new ChunkCoordIntPair(entry.getLongKey()); ++ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + " (" + r.x + "," + r.z + "): " + entry.getValue()); ++ } ++ } ++ } ++ ++ 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; ++ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + " " + nx + "," + nz + " in " + chunkHolder.getWorld().getWorld().getName() + ":"); ++ dumpChunkInfo(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 716d4eab382244ee9bc4855bf0f026e65ec057d6..20052dcbc71899165ac99801fa9f7753672ba997 100644 +index 586a20fe5c77c2ad5fa26f337a94a16e21d8b5e2..58407f488bbd8bda8781959d7c9da5d09f2a3cc4 100644 --- a/src/main/java/net/minecraft/server/ChunkMapDistance.java +++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java -@@ -143,7 +143,7 @@ public abstract class ChunkMapDistance { +@@ -154,7 +154,7 @@ public abstract class ChunkMapDistance { Ticket ticket1 = (Ticket) arraysetsorted.a(ticket); // CraftBukkit - decompile error ticket1.a(this.currentTick); - if (ticket.b() < j) { -+ if (ticket.b() < j || (ticket.getTicketType() == TicketType.PRIORITY && ((Ticket) ticket).getObjectReason() < j)) { // Paper - check priority tickets too ++ if (ticket.b() < j || (ticket.getTicketType() == TicketType.PRIORITY && (30 - ticket.priority) < j)) { // Paper - check priority tickets too this.e.b(i, ticket.b(), true); } -@@ -171,6 +171,48 @@ public abstract class ChunkMapDistance { +@@ -182,6 +182,54 @@ public abstract class ChunkMapDistance { this.addTicketAtLevel(tickettype, chunkcoordintpair, i, t0); } @@ -45,8 +93,15 @@ index 716d4eab382244ee9bc4855bf0f026e65ec057d6..20052dcbc71899165ac99801fa9f7753 + } + public boolean markHighPriority(ChunkCoordIntPair coords, int priority) { + priority = Math.min(30, Math.max(1, priority)); -+ Ticket ticket = new Ticket(TicketType.PRIORITY, 31, priority); -+ return this.addTicket(coords.pair(), ticket); ++ long pair = coords.pair(); ++ int currentPriority = getChunkPriority(coords); ++ if (currentPriority > priority) { ++ return false; ++ } ++ Ticket ticket = new Ticket(TicketType.PRIORITY, 31, 0); ++ ticket.priority = priority; ++ this.removeTicket(pair, ticket); ++ return this.addTicket(pair, ticket); + } + public int getChunkPriority(ChunkCoordIntPair coords) { + int priority = 0; @@ -55,36 +110,35 @@ index 716d4eab382244ee9bc4855bf0f026e65ec057d6..20052dcbc71899165ac99801fa9f7753 + return priority; + } + for (Ticket ticket : tickets) { -+ if (ticket.getTicketType() != TicketType.PRIORITY) { -+ continue; -+ } -+ //noinspection unchecked -+ Ticket prioTicket = (Ticket) ticket; -+ if (prioTicket.getObjectReason() > priority) { -+ priority = prioTicket.getObjectReason(); ++ if (ticket.getTicketType() == TicketType.PRIORITY && ticket.priority > 0) { ++ return ticket.priority; + } + } + return priority; + } -+ public void clearPriorityTickets(ChunkCoordIntPair coords) { ++ ++ public void refreshUrgentTicket(ChunkCoordIntPair coords) { + ArraySetSorted> tickets = this.tickets.get(coords.pair()); -+ java.util.List> toRemove = new java.util.ArrayList<>(); -+ if (tickets == null) return; ++ if (tickets == null) { ++ markUrgent(coords); ++ return; ++ } + for (Ticket ticket : tickets) { + if (ticket.getTicketType() == TicketType.PRIORITY) { -+ toRemove.add(ticket); ++ ticket.setCurrentTick(this.currentTick); ++ return; + } + } -+ for (Ticket ticket : toRemove) { -+ this.removeTicket(coords.pair(), ticket); -+ } + + } ++ public void clearPriorityTickets(ChunkCoordIntPair coords) { ++ this.removeTicket(coords.pair(), new Ticket(TicketType.PRIORITY, 31, 0)); ++ } + // Paper end public boolean addTicketAtLevel(TicketType ticketType, ChunkCoordIntPair chunkcoordintpair, int level, T identifier) { return this.addTicket(chunkcoordintpair.pair(), new Ticket<>(ticketType, level, identifier)); // CraftBukkit end -@@ -386,7 +428,8 @@ public abstract class ChunkMapDistance { +@@ -397,7 +445,8 @@ public abstract class ChunkMapDistance { }); }, i, () -> { @@ -95,7 +149,7 @@ index 716d4eab382244ee9bc4855bf0f026e65ec057d6..20052dcbc71899165ac99801fa9f7753 } else { ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 0a99b347d8497f097ef1da6560a5d0adc1374f25..d6f629d45e05167c22b6cd08a9709809a32b15a1 100644 +index 0a99b347d8497f097ef1da6560a5d0adc1374f25..e57e4c739b86646ef148c1f8e06ca160dbc778a1 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -431,6 +431,16 @@ public class ChunkProviderServer extends IChunkProvider { @@ -115,7 +169,7 @@ index 0a99b347d8497f097ef1da6560a5d0adc1374f25..d6f629d45e05167c22b6cd08a9709809 // Paper end @Nullable -@@ -469,6 +479,8 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -469,14 +479,22 @@ public class ChunkProviderServer extends IChunkProvider { if (!completablefuture.isDone()) { // Paper // Paper start - async chunk io/loading @@ -124,15 +178,22 @@ index 0a99b347d8497f097ef1da6560a5d0adc1374f25..d6f629d45e05167c22b6cd08a9709809 this.world.asyncChunkTaskManager.raisePriority(x, z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.world, x, z); // Paper end -@@ -477,6 +489,7 @@ public class ChunkProviderServer extends IChunkProvider { - this.serverThreadQueue.awaitTasks(completablefuture::isDone); + com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.world, x, z); // Paper - sync load info + this.world.timings.syncChunkLoad.startTiming(); // Paper +- this.serverThreadQueue.awaitTasks(completablefuture::isDone); ++ // Paper start - keep priority ticket refreshed ++ this.serverThreadQueue.awaitTasks(() -> { ++ this.chunkMapDistance.refreshUrgentTicket(pair); ++ return completablefuture.isDone(); ++ }); ++ // PAper end com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug this.world.timings.syncChunkLoad.stopTiming(); // Paper + this.clearPriorityTickets(pair); // Paper } // Paper ichunkaccess = (IChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { return ichunkaccess1; -@@ -529,6 +542,7 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -529,6 +547,7 @@ public class ChunkProviderServer extends IChunkProvider { if (flag && !currentlyUnloading) { // CraftBukkit end this.chunkMapDistance.a(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair); @@ -140,7 +201,7 @@ index 0a99b347d8497f097ef1da6560a5d0adc1374f25..d6f629d45e05167c22b6cd08a9709809 if (this.a(playerchunk, l)) { GameProfilerFiller gameprofilerfiller = this.world.getMethodProfiler(); -@@ -541,8 +555,13 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -541,8 +560,13 @@ public class ChunkProviderServer extends IChunkProvider { } } } @@ -181,26 +242,36 @@ 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 9e95c6b54f0855ddde6db6bd3e768e87fee6c21a..44721a4f446bdd5bf4575e4168a0570afd71c4d4 100644 +index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..46462298c7d02fcf31bb8da502a3ee5d98fe7905 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -25,7 +25,7 @@ public class PlayerChunk { +@@ -26,8 +26,8 @@ public class PlayerChunk { private CompletableFuture chunkSave; public int oldTicketLevel; private int ticketLevel; - private int n; -+ private int n; public final int getCurrentPriority() { return n; } // Paper - OBFHELPER - final ChunkCoordIntPair location; // Paper - private -> package +- final ChunkCoordIntPair location; // Paper - private -> package ++ volatile int n; public final int getCurrentPriority() { return n; } // Paper - OBFHELPER - make volatile since this is concurrently accessed ++ public final ChunkCoordIntPair location; // Paper - private -> public private final short[] dirtyBlocks; private int dirtyCount; -@@ -68,6 +68,92 @@ public class PlayerChunk { + private int r; +@@ -40,6 +40,7 @@ public class PlayerChunk { + private boolean hasBeenLoaded; + + private final PlayerChunkMap chunkMap; // Paper ++ public WorldServer getWorld() { return chunkMap.world; } // Paper + + long lastAutoSaveTime; // Paper - incremental autosave + long inactiveTimeStart; // Paper - incremental autosave +@@ -67,6 +68,92 @@ public class PlayerChunk { return null; } // Paper end - no-tick view distance + // Paper start - Chunk gen/load priority system + volatile int neighborPriority = -1; -+ final java.util.concurrent.ConcurrentHashMap neighbors = new java.util.concurrent.ConcurrentHashMap<>(); -+ final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap neighborPriorities = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(); ++ public final java.util.concurrent.ConcurrentHashMap neighbors = new java.util.concurrent.ConcurrentHashMap<>(); ++ public final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap neighborPriorities = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(); + + public int getPreferredPriority() { + int priority = neighborPriority; // if we have a neighbor priority, use it @@ -221,10 +292,10 @@ index 9e95c6b54f0855ddde6db6bd3e768e87fee6c21a..44721a4f446bdd5bf4575e4168a0570a + return Math.max(1, Math.min(PlayerChunkMap.GOLDEN_TICKET, priority)); + } + public void onNeighborRequest(PlayerChunk neighbor, ChunkStatus status) { -+ int currentPriority = getCurrentPriority(); -+ if (!neighborPriorities.containsKey(neighbor.location.pair()) && (neighbor.neighborPriority == -1 || neighbor.neighborPriority > currentPriority)) { -+ this.neighbors.put(neighbor, currentPriority); -+ neighbor.setNeighborPriority(this, Math.max(1, currentPriority)); ++ int priority = getCurrentPriority() + 1; ++ if (!neighborPriorities.containsKey(neighbor.location.pair()) && (neighbor.neighborPriority == -1 || neighbor.neighborPriority > priority)) { ++ this.neighbors.put(neighbor, status); ++ neighbor.setNeighborPriority(this, Math.max(1, priority)); + } + } + @@ -286,7 +357,7 @@ index 9e95c6b54f0855ddde6db6bd3e768e87fee6c21a..44721a4f446bdd5bf4575e4168a0570a public PlayerChunk(ChunkCoordIntPair chunkcoordintpair, int i, LightEngine lightengine, PlayerChunk.c playerchunk_c, PlayerChunk.d playerchunk_d) { this.statusFutures = new AtomicReferenceArray(PlayerChunk.CHUNK_STATUSES.size()); -@@ -166,6 +252,12 @@ public class PlayerChunk { +@@ -165,6 +252,12 @@ public class PlayerChunk { } return null; } @@ -299,26 +370,15 @@ index 9e95c6b54f0855ddde6db6bd3e768e87fee6c21a..44721a4f446bdd5bf4575e4168a0570a // Paper end public CompletableFuture> getStatusFutureUnchecked(ChunkStatus chunkstatus) { -@@ -419,8 +511,18 @@ public class PlayerChunk { +@@ -418,6 +511,7 @@ public class PlayerChunk { return this.n; } + private void setPriority(int i) { d(i); } // Paper - OBFHELPER private void d(int i) { -+ if (i == n) return; // Paper this.n = i; -+ // Paper start -+ this.neighbors.keySet().forEach(neighbor -> { -+ if (neighbor.getCurrentPriority() > i) { -+ neighbor.setNeighborPriority(this, i); -+ this.w.changePriority(neighbor.location, neighbor::getCurrentPriority, neighbor.getCurrentPriority(), neighbor::setPriority); -+ } -+ }); -+ // Paper end } - - public void a(int i) { -@@ -508,6 +610,7 @@ public class PlayerChunk { +@@ -507,6 +601,7 @@ public class PlayerChunk { Chunk fullChunk = either.left().get(); PlayerChunk.this.isFullChunkReady = true; fullChunk.playerChunk = PlayerChunk.this; @@ -326,16 +386,40 @@ index 9e95c6b54f0855ddde6db6bd3e768e87fee6c21a..44721a4f446bdd5bf4575e4168a0570a } -@@ -583,7 +686,7 @@ public class PlayerChunk { +@@ -581,8 +676,30 @@ public class PlayerChunk { + this.entityTickingFuture.complete(PlayerChunk.UNLOADED_CHUNK); this.isEntityTickingReady = false; // Paper - cache chunk ticking stage this.entityTickingFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE; } - +- - this.w.a(this.location, this::k, this.ticketLevel, this::d); -+ this.w.a(this.location, this::k, getPreferredPriority(), this::d); // Paper - preferred priority ++ // Paper start - raise IO/load priority if priority changes, use our preferred priority ++ int priority = getPreferredPriority(); ++ if (getCurrentPriority() > priority) { ++ int ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY; ++ if (priority <= 10) { ++ ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY; ++ } else if (priority <= 20) { ++ ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY; ++ } ++ chunkMap.world.asyncChunkTaskManager.raisePriority(location.x, location.z, ioPriority); ++ } ++ this.w.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority ++ this.neighbors.forEach((neighbor, neighborDesired) -> { ++ ChunkStatus neighborCurrent = neighbor.getChunkHolderStatus(); ++ if (neighborCurrent == null || !neighborCurrent.isAtLeastStatus(neighborDesired)) { ++ if (neighbor.getCurrentPriority() > priority + 1 && neighbor.neighborPriority > priority + 1) { ++ neighbor.setNeighborPriority(this, priority + 1); ++ // Pending chunk update will run this same code here for the neighbor to update their priority ++ // And since we are in the poll loop when this method runs, it should happen immediately after this. ++ chunkMap.chunkDistanceManager.pendingChunkUpdates.add(neighbor); ++ } ++ } ++ }); ++ // 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. -@@ -670,6 +773,7 @@ public class PlayerChunk { +@@ -669,6 +786,7 @@ public class PlayerChunk { public interface c { @@ -489,6 +573,26 @@ index e772095e1c44842f743661a326c2a9a8a677ab02..5621416660d2722f26582fcecd5b61a1 }); } +diff --git a/src/main/java/net/minecraft/server/Ticket.java b/src/main/java/net/minecraft/server/Ticket.java +index 7a8397815a5b7f79f3e3a0348aeedf63fe879f8f..a7cd67b0d5e49a4492dc14ec80e442a0f32671d3 100644 +--- a/src/main/java/net/minecraft/server/Ticket.java ++++ b/src/main/java/net/minecraft/server/Ticket.java +@@ -8,6 +8,7 @@ public final class Ticket implements Comparable> { + private final int b; + public final T identifier; public final T getObjectReason() { return this.identifier; } // Paper - OBFHELPER + private long d; public final long getCreationTick() { return this.d; } // Paper - OBFHELPER ++ public int priority = 0; // Paper + + protected Ticket(TicketType tickettype, int i, T t0) { + this.a = tickettype; +@@ -56,6 +57,7 @@ public final class Ticket implements Comparable> { + return this.b; + } + ++ protected final void setCurrentTick(long i) { a(i); } // Paper - OBFHELPER + protected void a(long i) { + this.d = i; + } diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java index 8055f5998213ab1c6c10d03d88d2b14d220a5e40..4913205c15a2b6d5ea058890b02090b494e9c177 100644 --- a/src/main/java/net/minecraft/server/TicketType.java