From bfc807c27af524162a56c41bcceb62358f640a80 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sun, 29 Sep 2019 18:23:09 -0700
Subject: [PATCH] Improve spawn loading and debug output for waiting on a chunk
 (#2595)

Spawn loading has been changed to use getChunkAt calls to manually
load chunks since watchdog can watch these calls and so we guard
against plugins/players changing the radius of a spawn while it's
loading

Debug output has been improved to note the status of the currently
waiting chunk
---
 ...le-Keep-Spawn-Loaded-range-per-world.patch | 53 +++++++++++--------
 .../0391-incremental-chunk-saving.patch       |  6 +--
 ...09-Asynchronous-chunk-IO-and-loading.patch | 37 ++++++++++---
 3 files changed, 64 insertions(+), 32 deletions(-)

diff --git a/Spigot-Server-Patches/0385-Configurable-Keep-Spawn-Loaded-range-per-world.patch b/Spigot-Server-Patches/0385-Configurable-Keep-Spawn-Loaded-range-per-world.patch
index 088374345e..efeb226fa1 100644
--- a/Spigot-Server-Patches/0385-Configurable-Keep-Spawn-Loaded-range-per-world.patch
+++ b/Spigot-Server-Patches/0385-Configurable-Keep-Spawn-Loaded-range-per-world.patch
@@ -1,4 +1,4 @@
-From b4155a3581c39d61d4eab95e5a6103d77924fe37 Mon Sep 17 00:00:00 2001
+From e33d861d00db79f27822b8fdb5dff1184282f8f6 Mon Sep 17 00:00:00 2001
 From: Aikar <aikar@aikar.co>
 Date: Sat, 13 Sep 2014 23:14:43 -0400
 Subject: [PATCH] Configurable Keep Spawn Loaded range per world
@@ -6,7 +6,7 @@ Subject: [PATCH] Configurable Keep Spawn Loaded range per world
 This lets you disable it for some worlds and lower it for others.
 
 diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
-index d8bb13693..de11a91af 100644
+index d8bb13693d..de11a91af6 100644
 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
 +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
 @@ -483,4 +483,10 @@ public class PaperWorldConfig {
@@ -21,47 +21,58 @@ index d8bb13693..de11a91af 100644
 +    }
  }
 diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index c70ab3caf..c58f6f50d 100644
+index ee02001700..a6f112bd0f 100644
 --- a/src/main/java/net/minecraft/server/MinecraftServer.java
 +++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -577,6 +577,13 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
+@@ -577,6 +577,14 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
          this.forceTicks = true;
          // CraftBukkit end
  
 +        // Paper start - configurable spawn reason
 +        int radiusBlocks = worldserver.paperConfig.keepLoadedRange;
-+        worldloadlistener.setChunkRadius(radiusBlocks / 16);
-+        int totalChunks = ((radiusBlocks / 16) * 2 + 1);
++        int radiusChunks = radiusBlocks / 16 + ((radiusBlocks & 15) != 0 ? 1 : 0);
++        int totalChunks = ((radiusChunks) * 2 + 1);
 +        totalChunks *= totalChunks;
++        worldloadlistener.setChunkRadius(radiusBlocks / 16);
 +        // Paper end
 +
          MinecraftServer.LOGGER.info("Preparing start region for dimension '{}'/{}", worldserver.getWorldData().getName(), DimensionManager.a(worldserver.worldProvider.getDimensionManager().getType())); // CraftBukkit
          BlockPosition blockposition = worldserver.getSpawn();
  
-@@ -585,14 +592,19 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
+@@ -585,14 +593,24 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
  
          chunkproviderserver.getLightEngine().a(500);
          this.nextTick = SystemUtils.getMonotonicMillis();
 -        chunkproviderserver.addTicket(TicketType.START, new ChunkCoordIntPair(blockposition), 11, Unit.INSTANCE);
+-
+-        while (chunkproviderserver.b() != 441) {
+-            // CraftBukkit start
+-            // this.nextTick = SystemUtils.getMonotonicMillis() + 10L;
+-            this.executeModerately();
+-            // CraftBukkit end
 +        // Paper start - Configurable spawn radius
 +        if (worldserver.keepSpawnInMemory) {
 +            worldserver.addTicketsForSpawn(radiusBlocks, blockposition);
-+        }
- 
--        while (chunkproviderserver.b() != 441) {
-+        while (worldserver.keepSpawnInMemory && chunkproviderserver.b() != totalChunks) {
-+            // Paper end
-             // CraftBukkit start
-             // this.nextTick = SystemUtils.getMonotonicMillis() + 10L;
-             this.executeModerately();
-             // CraftBukkit end
++
++            // we use a getChunk loop since we don't need to worry about what some plugin does to keepSpawnInMemory
++            // or the spawn radius while we are loading
++            // just keep in mind too that executeModerately will handle player network queue (i.e commands)
++            int centerX = blockposition.getX() >> 4;
++            int centerZ = blockposition.getZ() >> 4;
++            radiusChunks += 2; // we need to load radius +2 to get the chunks in ticking level
++            for (int xoff = -radiusChunks; xoff <= radiusChunks; ++xoff) {
++                for (int zoff = -radiusChunks; zoff <= radiusChunks; ++zoff) {
++                    worldserver.getChunkAt(centerX + xoff, centerZ + zoff);
++                }
++            }
          }
++        // Paper end
 +        LOGGER.info("Loaded " + chunkproviderserver.b() + " spawn chunks for world " + worldserver.getWorldData().getName()); // Paper
  
          // CraftBukkit start
          // this.nextTick = SystemUtils.getMonotonicMillis() + 10L;
 diff --git a/src/main/java/net/minecraft/server/WorldLoadListener.java b/src/main/java/net/minecraft/server/WorldLoadListener.java
-index d6762d385..7b6f5b2da 100644
+index d6762d3853..7b6f5b2da0 100644
 --- a/src/main/java/net/minecraft/server/WorldLoadListener.java
 +++ b/src/main/java/net/minecraft/server/WorldLoadListener.java
 @@ -9,4 +9,6 @@ public interface WorldLoadListener {
@@ -72,7 +83,7 @@ index d6762d385..7b6f5b2da 100644
 +    void setChunkRadius(int radius); // Paper - allow changing chunk radius
  }
 diff --git a/src/main/java/net/minecraft/server/WorldLoadListenerLogger.java b/src/main/java/net/minecraft/server/WorldLoadListenerLogger.java
-index 3868572ae..ae77805f7 100644
+index 3868572aed..ae77805f71 100644
 --- a/src/main/java/net/minecraft/server/WorldLoadListenerLogger.java
 +++ b/src/main/java/net/minecraft/server/WorldLoadListenerLogger.java
 @@ -7,16 +7,24 @@ import org.apache.logging.log4j.Logger;
@@ -103,7 +114,7 @@ index 3868572ae..ae77805f7 100644
      @Override
      public void a(ChunkCoordIntPair chunkcoordintpair) {
 diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
-index 0f8f54d8e..8a3124fed 100644
+index 0f8f54d8e9..8a3124fed4 100644
 --- a/src/main/java/net/minecraft/server/WorldServer.java
 +++ b/src/main/java/net/minecraft/server/WorldServer.java
 @@ -1525,13 +1525,85 @@ public class WorldServer extends World {
@@ -196,7 +207,7 @@ index 0f8f54d8e..8a3124fed 100644
  
      public LongSet getForceLoadedChunks() {
 diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 38939ce81..0c31c349a 100644
+index 38939ce81e..0c31c349ab 100644
 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
 +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
 @@ -1853,15 +1853,21 @@ public class CraftWorld implements World {
@@ -226,5 +237,5 @@ index 38939ce81..0c31c349a 100644
  
      @Override
 -- 
-2.23.0
+2.22.1
 
diff --git a/Spigot-Server-Patches/0391-incremental-chunk-saving.patch b/Spigot-Server-Patches/0391-incremental-chunk-saving.patch
index 2c2ab4b4ff..98edfea516 100644
--- a/Spigot-Server-Patches/0391-incremental-chunk-saving.patch
+++ b/Spigot-Server-Patches/0391-incremental-chunk-saving.patch
@@ -1,4 +1,4 @@
-From 2b993316234835b83c786522f6a71a120f030618 Mon Sep 17 00:00:00 2001
+From 52ddb3aedd0e33aa0f7ea8aa8013cbdfa20031d2 Mon Sep 17 00:00:00 2001
 From: Shane Freeder <theboyetronic@gmail.com>
 Date: Sun, 9 Jun 2019 03:53:22 +0100
 Subject: [PATCH] incremental chunk saving
@@ -62,7 +62,7 @@ index 02dfd91c5e..8689e0f9f0 100644
      public void close() throws IOException {
          // CraftBukkit start
 diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 098b3e5355..7e49d21625 100644
+index a6f112bd0f..5238a1a7ca 100644
 --- a/src/main/java/net/minecraft/server/MinecraftServer.java
 +++ b/src/main/java/net/minecraft/server/MinecraftServer.java
 @@ -165,6 +165,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
@@ -73,7 +73,7 @@ index 098b3e5355..7e49d21625 100644
      public File bukkitDataPackFolder;
      public CommandDispatcher vanillaCommandDispatcher;
      private boolean forceTicks;
-@@ -1102,14 +1103,28 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
+@@ -1108,14 +1109,28 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
              this.serverPing.b().a(agameprofile);
          }
  
diff --git a/Spigot-Server-Patches/0409-Asynchronous-chunk-IO-and-loading.patch b/Spigot-Server-Patches/0409-Asynchronous-chunk-IO-and-loading.patch
index fbee2fa5d6..63ec0d377a 100644
--- a/Spigot-Server-Patches/0409-Asynchronous-chunk-IO-and-loading.patch
+++ b/Spigot-Server-Patches/0409-Asynchronous-chunk-IO-and-loading.patch
@@ -1,4 +1,4 @@
-From 9413728661866351fccf0d24d8c332f7e53c9ec1 Mon Sep 17 00:00:00 2001
+From 25eb4ce4491f0ab4ed97614dc5de32690bd815fb Mon Sep 17 00:00:00 2001
 From: Spottedleaf <Spottedleaf@users.noreply.github.com>
 Date: Sat, 13 Jul 2019 09:23:10 -0700
 Subject: [PATCH] Asynchronous chunk IO and loading
@@ -1901,10 +1901,10 @@ index 0000000000..1dfa8abfd8
 +}
 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 0000000000..1d69715e26
+index 0000000000..59d73bfad7
 --- /dev/null
 +++ b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java
-@@ -0,0 +1,441 @@
+@@ -0,0 +1,453 @@
 +package com.destroystokyo.paper.io.chunk;
 +
 +import com.destroystokyo.paper.io.PaperFileIOThread;
@@ -1992,12 +1992,24 @@ index 0000000000..1d69715e26
 +            PaperFileIOThread.LOGGER.log(Level.ERROR, "Chunk wait task info below: ");
 +
 +            for (final ChunkInfo chunkInfo : WAITING_CHUNKS) {
-+                final ChunkLoadTask loadTask = chunkInfo.world.asyncChunkTaskManager.chunkLoadTasks.get(IOUtil.getCoordinateKey(chunkInfo.chunkX, chunkInfo.chunkZ));
-+                final ChunkSaveTask saveTask = chunkInfo.world.asyncChunkTaskManager.chunkSaveTasks.get(IOUtil.getCoordinateKey(chunkInfo.chunkX, chunkInfo.chunkZ));
++                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);
 +
 +                PaperFileIOThread.LOGGER.log(Level.ERROR, chunkInfo.chunkX + "," + chunkInfo.chunkZ + " in '" + chunkInfo.world.getWorld().getName() + ":");
 +                PaperFileIOThread.LOGGER.log(Level.ERROR, "Load Task - " + (loadTask == null ? "none" : loadTask.toString()));
 +                PaperFileIOThread.LOGGER.log(Level.ERROR, "Save Task - " + (saveTask == null ? "none" : saveTask.toString()));
++                // 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();
++                    PaperFileIOThread.LOGGER.log(Level.ERROR, "Chunk Holder - non-null");
++                    PaperFileIOThread.LOGGER.log(Level.ERROR, "Chunk Status - " + ((chunk == null) ? "null chunk" : chunk.getChunkStatus().toString()));
++                }
++
 +            }
 +        }
 +    }
@@ -2897,10 +2909,10 @@ index 23d1935dd5..14f8b61042 100644
 +    }
  }
 diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index ccf359dff1..a256f043ad 100644
+index 5238a1a7ca..0b00581382 100644
 --- a/src/main/java/net/minecraft/server/MinecraftServer.java
 +++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -774,6 +774,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
+@@ -780,6 +780,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
              this.getUserCache().c(false); // Paper
          }
          // Spigot end
@@ -2947,7 +2959,7 @@ index af934ef8bc..34d0ab0d5e 100644
                  completablefuture = (CompletableFuture) this.statusFutures.get(i);
                  if (completablefuture != null) {
 diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
-index fd0d2b6e67..9daf64bad4 100644
+index fd0d2b6e67..31d106f951 100644
 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
 +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
 @@ -62,7 +62,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
@@ -2977,6 +2989,15 @@ index fd0d2b6e67..9daf64bad4 100644
          this.setViewDistance(i);
      }
  
+@@ -180,7 +180,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
+     }
+ 
+     @Nullable
+-    protected PlayerChunk getVisibleChunk(long i) {
++    public PlayerChunk getVisibleChunk(long i) { // Paper - protected -> public
+         return (PlayerChunk) this.visibleChunks.get(i);
+     }
+ 
 @@ -293,6 +293,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
      @Override
      public void close() throws IOException {