diff --git a/Spigot-Server-Patches/0400-incremental-chunk-saving.patch b/Spigot-Server-Patches/0400-incremental-chunk-saving.patch new file mode 100644 index 0000000000..186ac1af87 --- /dev/null +++ b/Spigot-Server-Patches/0400-incremental-chunk-saving.patch @@ -0,0 +1,167 @@ +From c5851b2a5288702fd63f5ab5d199868ab6222d48 Mon Sep 17 00:00:00 2001 +From: Shane Freeder +Date: Sun, 9 Jun 2019 03:53:22 +0100 +Subject: [PATCH] incremental chunk saving + + +diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +index b854061983..58109e1308 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +@@ -494,4 +494,19 @@ public class PaperWorldConfig { + keepLoadedRange = (short) (getInt("keep-spawn-loaded-range", Math.min(spigotConfig.viewDistance, 10)) * 16); + log( "Keep Spawn Loaded Range: " + (keepLoadedRange/16)); + } ++ ++ public int autoSavePeriod = -1; ++ private void autoSavePeriod() { ++ autoSavePeriod = getInt("auto-save-interval", -1); ++ if (autoSavePeriod > 0) { ++ log("Auto Save Interval: " +autoSavePeriod + " (" + (autoSavePeriod / 20) + "s)"); ++ } else if (autoSavePeriod < 0) { ++ autoSavePeriod = net.minecraft.server.MinecraftServer.getServer().autosavePeriod; ++ } ++ } ++ ++ public int maxAutoSaveChunksPerTick = 24; ++ private void maxAutoSaveChunksPerTick() { ++ maxAutoSaveChunksPerTick = getInt("max-auto-save-chunks-per-tick", 24); ++ } + } +diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java +index ef07f665b7..d89ad4e390 100644 +--- a/src/main/java/net/minecraft/server/Chunk.java ++++ b/src/main/java/net/minecraft/server/Chunk.java +@@ -42,7 +42,7 @@ public class Chunk implements IChunkAccess { + private TickList o; + private TickList p; + private boolean q; +- private long lastSaved; ++ public long lastSaved; // Paper + private volatile boolean s; + private long t; + @Nullable +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 184f1b00f0..13e862aa9f 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -156,6 +156,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); + public int autosavePeriod; ++ public boolean serverAutoSave = false; // Paper + public File bukkitDataPackFolder; + public CommandDispatcher vanillaCommandDispatcher; + private boolean forceTicks; +@@ -1072,14 +1073,22 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant 0 && this.ticks % autosavePeriod == 0) { // CraftBukkit ++ //if (autosavePeriod > 0 && this.ticks % autosavePeriod == 0) { // CraftBukkit // Paper - move down + MinecraftServer.LOGGER.debug("Autosave started"); ++ serverAutoSave = (autosavePeriod > 0 && this.ticks % autosavePeriod == 0); // Paper + this.methodProfiler.enter("save"); ++ if (autosavePeriod > 0 && this.ticks % autosavePeriod == 0) { // Paper + this.playerList.savePlayers(); +- this.saveChunks(true, false, false); ++ }// Paper ++ // Paper start ++ for (World world : getWorlds()) { ++ if (world.paperConfig.autoSavePeriod > 0) world.getWorld().save(false); ++ } ++ // Paper end ++ + this.methodProfiler.exit(); + MinecraftServer.LOGGER.debug("Autosave finished"); +- } ++ //} // Paper + + 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/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java +index c091007d8c..67aa9325b6 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java ++++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java +@@ -295,15 +295,29 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + }); + PlayerChunkMap.LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", this.x.getName()); + } else { +- this.visibleChunks.values().stream().filter(PlayerChunk::hasBeenLoaded).forEach((playerchunk) -> { ++ // Paper start ++ int savedThisTick = 0; ++ for (PlayerChunk playerchunk : this.visibleChunks.values()) { ++ if (!playerchunk.hasBeenLoaded()) continue; ++ // Paper end + IChunkAccess ichunkaccess = (IChunkAccess) playerchunk.getChunkSave().getNow(null); // CraftBukkit - decompile error + + if (ichunkaccess instanceof ProtoChunkExtension || ichunkaccess instanceof Chunk) { +- this.saveChunk(ichunkaccess); ++ // paper start ++ boolean shouldSave = true; ++ ++ if (ichunkaccess instanceof Chunk) { ++ shouldSave = ((Chunk) ichunkaccess).lastSaved + world.paperConfig.autoSavePeriod <= world.getTime(); ++ } ++ ++ if (shouldSave && this.saveChunk(ichunkaccess)) ++savedThisTick; + playerchunk.l(); ++ if (savedThisTick >= world.paperConfig.maxAutoSaveChunksPerTick) { ++ return; ++ } + } +- +- }); ++ }; ++ // paper end + } + + } +diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java +index 92aad060ef..c650f40b0d 100644 +--- a/src/main/java/net/minecraft/server/WorldServer.java ++++ b/src/main/java/net/minecraft/server/WorldServer.java +@@ -757,6 +757,7 @@ public class WorldServer extends World { + if (!flag1) { + org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); // CraftBukkit + try (co.aikar.timings.Timing ignored = timings.worldSave.startTiming()) { // Paper ++ if (flag || server.serverAutoSave) { // Paper + if (iprogressupdate != null) { + iprogressupdate.a(new ChatMessage("menu.savingLevel", new Object[0])); + } +@@ -765,6 +766,7 @@ public class WorldServer extends World { + if (iprogressupdate != null) { + iprogressupdate.c(new ChatMessage("menu.savingChunks", new Object[0])); + } ++ } // Paper + + timings.worldSaveChunks.startTiming(); // Paper + chunkproviderserver.save(flag); +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 0c53795306..4e60931cd8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -1151,13 +1151,19 @@ public class CraftWorld implements World { + // Paper end + + @Override ++ // Paper start + public void save() { ++ save(true); ++ } ++ ++ public void save(boolean forceSave) { ++ // Paper end + this.server.checkSaveState(); + try { + boolean oldSave = world.savingDisabled; + + world.savingDisabled = false; +- world.save(null, false, false); ++ world.save(null, forceSave, false); // Paper + + world.savingDisabled = oldSave; + } catch (ExceptionWorldConflict ex) { +-- +2.21.0 +