mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-11 09:23:42 +01:00
b31089a929
Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: d264e972 #591: Add option for a consumer before spawning an item 1c537fce #590: Add spawn and transform reasons for piglin zombification. CraftBukkit Changes: ee5006d1 #810: Add option for a consumer before spawning an item f6a39d3c #809: Add spawn and transform reasons for piglin zombification. 0c24068a Organise imports Spigot Changes: bff52619 Organise imports
323 lines
15 KiB
Diff
323 lines
15 KiB
Diff
From 0000000000000000000000000000000000000000 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
|
|
|
|
|
|
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
index ffe9b1a63d78925e1d77b9e730aef42fed6d58fa..1278d09f70c1e97607ef20d87a178dc252c7f723 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
@@ -446,4 +446,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 b258d4a85c41c2de55a28c565766f57c3f1604ff..fa549cd2ab9d0665dc042f3d4e79e205eaf4da54 100644
|
|
--- a/src/main/java/net/minecraft/server/Chunk.java
|
|
+++ b/src/main/java/net/minecraft/server/Chunk.java
|
|
@@ -43,7 +43,7 @@ public class Chunk implements IChunkAccess {
|
|
private TickList<Block> o;
|
|
private TickList<FluidType> p;
|
|
private boolean q;
|
|
- private long lastSaved;
|
|
+ public long lastSaved; // Paper
|
|
private volatile boolean s;
|
|
private long inhabitedTime;
|
|
@Nullable
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
index d340990000144d9500069824e7e6609400a54ece..71c7bf2fd4fdb0ad880ca81c198a7cf25cdb9e87 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -535,6 +535,15 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
} // Paper - Timings
|
|
}
|
|
|
|
+ // Paper start - duplicate save, but call incremental
|
|
+ public void saveIncrementally() {
|
|
+ this.tickDistanceManager();
|
|
+ try (co.aikar.timings.Timing timed = world.timings.chunkSaveData.startTiming()) { // Paper - Timings
|
|
+ this.playerChunkMap.saveIncrementally();
|
|
+ } // Paper - Timings
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
@Override
|
|
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 ac512c5a1bd10b119a07c11dc6f8569005348ac8..7f63e60caac65a24909566e3f9a0f5ba5aa1575a 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -154,6 +154,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
|
|
public static int currentTick = 0; // Paper - Further improve tick loop
|
|
public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
|
|
public int autosavePeriod;
|
|
+ public boolean serverAutoSave = false; // Paper
|
|
public CommandDispatcher vanillaCommandDispatcher;
|
|
private boolean forceTicks;
|
|
// CraftBukkit end
|
|
@@ -1139,14 +1140,24 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
|
|
this.serverPing.b().a(agameprofile);
|
|
}
|
|
|
|
- if (autosavePeriod > 0 && this.ticks % autosavePeriod == 0) { // CraftBukkit
|
|
- MinecraftServer.LOGGER.debug("Autosave started");
|
|
+ //if (autosavePeriod > 0 && this.ticks % autosavePeriod == 0) { // CraftBukkit // Paper - move down
|
|
+ //MinecraftServer.LOGGER.debug("Autosave started"); // Paper
|
|
+ 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 (WorldServer world : getWorlds()) {
|
|
+ if (world.paperConfig.autoSavePeriod > 0) {
|
|
+ world.saveIncrementally(serverAutoSave);
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
this.methodProfiler.exit();
|
|
- MinecraftServer.LOGGER.debug("Autosave finished");
|
|
- }
|
|
+ //MinecraftServer.LOGGER.debug("Autosave finished"); // Paper
|
|
+ //} // 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/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
index 2d749c07b159604e1ab904e026adf326eb9a732e..ad01b9333e73c8e4a7c848edc4ce252a51ea52b6 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
@@ -42,6 +42,9 @@ public class PlayerChunk {
|
|
|
|
private final PlayerChunkMap chunkMap; // Paper
|
|
|
|
+ long lastAutoSaveTime; // Paper - incremental autosave
|
|
+ long inactiveTimeStart; // Paper - incremental autosave
|
|
+
|
|
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;
|
|
@@ -397,7 +400,19 @@ public class PlayerChunk {
|
|
boolean flag2 = playerchunk_state.isAtLeast(PlayerChunk.State.BORDER);
|
|
boolean flag3 = playerchunk_state1.isAtLeast(PlayerChunk.State.BORDER);
|
|
|
|
+ boolean prevHasBeenLoaded = this.hasBeenLoaded; // Paper
|
|
this.hasBeenLoaded |= flag3;
|
|
+ // Paper start - incremental autosave
|
|
+ if (this.hasBeenLoaded & !prevHasBeenLoaded) {
|
|
+ long timeSinceAutoSave = this.inactiveTimeStart - this.lastAutoSaveTime;
|
|
+ if (timeSinceAutoSave < 0) {
|
|
+ // safest bet is to assume autosave is needed here
|
|
+ timeSinceAutoSave = this.chunkMap.world.paperConfig.autoSavePeriod;
|
|
+ }
|
|
+ this.lastAutoSaveTime = this.chunkMap.world.getTime() - timeSinceAutoSave;
|
|
+ this.chunkMap.autoSaveQueue.add(this);
|
|
+ }
|
|
+ // Paper end
|
|
if (!flag2 && flag3) {
|
|
// Paper start - cache ticking ready status
|
|
int expectCreateCount = ++this.fullChunkCreateCount;
|
|
@@ -517,8 +532,32 @@ public class PlayerChunk {
|
|
}
|
|
|
|
public void m() {
|
|
+ boolean prev = this.hasBeenLoaded; // Paper
|
|
+ this.hasBeenLoaded = getChunkState(this.ticketLevel).isAtLeast(PlayerChunk.State.BORDER);
|
|
+ // Paper start - incremental autosave
|
|
+ if (prev != this.hasBeenLoaded) {
|
|
+ if (this.hasBeenLoaded) {
|
|
+ long timeSinceAutoSave = this.inactiveTimeStart - this.lastAutoSaveTime;
|
|
+ if (timeSinceAutoSave < 0) {
|
|
+ // safest bet is to assume autosave is needed here
|
|
+ timeSinceAutoSave = this.chunkMap.world.paperConfig.autoSavePeriod;
|
|
+ }
|
|
+ this.lastAutoSaveTime = this.chunkMap.world.getTime() - timeSinceAutoSave;
|
|
+ this.chunkMap.autoSaveQueue.add(this);
|
|
+ } else {
|
|
+ this.inactiveTimeStart = this.chunkMap.world.getTime();
|
|
+ this.chunkMap.autoSaveQueue.remove(this);
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+ }
|
|
+
|
|
+ // Paper start - incremental autosave
|
|
+ public boolean setHasBeenLoaded() {
|
|
this.hasBeenLoaded = getChunkState(this.ticketLevel).isAtLeast(PlayerChunk.State.BORDER);
|
|
+ return this.hasBeenLoaded;
|
|
}
|
|
+ // Paper end
|
|
|
|
public void a(ProtoChunkExtension protochunkextension) {
|
|
for (int i = 0; i < this.statusFutures.length(); ++i) {
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
index 14288d7beaa9aea531b7eee16a4be4101d0380fa..abe94c9b7262a67de1a93914d94550f5d0abed28 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
@@ -47,6 +47,7 @@ import java.util.function.Supplier;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
import javax.annotation.Nullable;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; // Paper
|
|
import org.apache.commons.lang3.mutable.MutableBoolean;
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
@@ -334,6 +335,64 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
|
|
}
|
|
|
|
+ // Paper start - incremental autosave
|
|
+ final ObjectRBTreeSet<PlayerChunk> autoSaveQueue = new ObjectRBTreeSet<>((playerchunk1, playerchunk2) -> {
|
|
+ int timeCompare = Long.compare(playerchunk1.lastAutoSaveTime, playerchunk2.lastAutoSaveTime);
|
|
+ if (timeCompare != 0) {
|
|
+ return timeCompare;
|
|
+ }
|
|
+
|
|
+ return Long.compare(MCUtil.getCoordinateKey(playerchunk1.location), MCUtil.getCoordinateKey(playerchunk2.location));
|
|
+ });
|
|
+
|
|
+ protected void saveIncrementally() {
|
|
+ int savedThisTick = 0;
|
|
+ // optimized since we search far less chunks to hit ones that need to be saved
|
|
+ List<PlayerChunk> reschedule = new java.util.ArrayList<>(this.world.paperConfig.maxAutoSaveChunksPerTick);
|
|
+ long currentTick = this.world.getTime();
|
|
+ long maxSaveTime = currentTick - this.world.paperConfig.autoSavePeriod;
|
|
+
|
|
+ for (Iterator<PlayerChunk> iterator = this.autoSaveQueue.iterator(); iterator.hasNext();) {
|
|
+ PlayerChunk playerchunk = iterator.next();
|
|
+ if (playerchunk.lastAutoSaveTime > maxSaveTime) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ iterator.remove();
|
|
+
|
|
+ IChunkAccess ichunkaccess = playerchunk.getChunkSave().getNow(null);
|
|
+ if (ichunkaccess instanceof Chunk) {
|
|
+ boolean shouldSave = ((Chunk)ichunkaccess).lastSaved <= maxSaveTime;
|
|
+
|
|
+ if (shouldSave && this.saveChunk(ichunkaccess)) {
|
|
+ ++savedThisTick;
|
|
+
|
|
+ if (!playerchunk.setHasBeenLoaded()) {
|
|
+ // do not fall through to reschedule logic
|
|
+ playerchunk.inactiveTimeStart = currentTick;
|
|
+ if (savedThisTick >= this.world.paperConfig.maxAutoSaveChunksPerTick) {
|
|
+ break;
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ reschedule.add(playerchunk);
|
|
+
|
|
+ if (savedThisTick >= this.world.paperConfig.maxAutoSaveChunksPerTick) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (int i = 0, len = reschedule.size(); i < len; ++i) {
|
|
+ PlayerChunk playerchunk = reschedule.get(i);
|
|
+ playerchunk.lastAutoSaveTime = this.world.getTime();
|
|
+ this.autoSaveQueue.add(playerchunk);
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
protected void save(boolean flag) {
|
|
if (flag) {
|
|
List<PlayerChunk> list = (List) this.visibleChunks.values().stream().filter(PlayerChunk::hasBeenLoaded).peek(PlayerChunk::m).collect(Collectors.toList());
|
|
@@ -444,6 +503,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
|
|
this.world.unloadChunk(chunk);
|
|
}
|
|
+ this.autoSaveQueue.remove(playerchunk); // Paper
|
|
|
|
this.lightEngine.a(ichunkaccess.getPos());
|
|
this.lightEngine.queueUpdate();
|
|
@@ -636,6 +696,8 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
playerchunk.a(new ProtoChunkExtension(chunk));
|
|
}
|
|
|
|
+ chunk.setLastSaved(this.world.getTime() - 1); // Paper - avoid autosaving newly generated/loaded chunks
|
|
+
|
|
chunk.a(() -> {
|
|
return PlayerChunk.getChunkState(playerchunk.getTicketLevel());
|
|
});
|
|
diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
|
|
index 3a95b98b77171b4b18a1d939eb14953d24b903e0..3f97befa2742a575b103b68b19fc6415bfbd8c04 100644
|
|
--- a/src/main/java/net/minecraft/server/WorldServer.java
|
|
+++ b/src/main/java/net/minecraft/server/WorldServer.java
|
|
@@ -776,11 +776,43 @@ public class WorldServer extends World implements GeneratorAccessSeed {
|
|
return !this.server.a(this, blockposition, entityhuman) && this.getWorldBorder().a(blockposition);
|
|
}
|
|
|
|
+ // Paper start - derived from below
|
|
+ public void saveIncrementally(boolean doFull) {
|
|
+ ChunkProviderServer chunkproviderserver = this.getChunkProvider();
|
|
+
|
|
+ if (doFull) {
|
|
+ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld()));
|
|
+ }
|
|
+
|
|
+ try (co.aikar.timings.Timing ignored = timings.worldSave.startTiming()) {
|
|
+ if (doFull) {
|
|
+ this.saveData();
|
|
+ }
|
|
+
|
|
+ timings.worldSaveChunks.startTiming(); // Paper
|
|
+ if (!this.isSavingDisabled()) chunkproviderserver.saveIncrementally();
|
|
+ timings.worldSaveChunks.stopTiming(); // Paper
|
|
+
|
|
+
|
|
+ // Copied from save()
|
|
+ // CraftBukkit start - moved from MinecraftServer.saveChunks
|
|
+ if (doFull) { // Paper
|
|
+ WorldServer worldserver1 = this;
|
|
+
|
|
+ worldDataServer.a(worldserver1.getWorldBorder().t());
|
|
+ worldDataServer.setCustomBossEvents(this.server.getBossBattleCustomData().save());
|
|
+ convertable.a(this.server.customRegistry, this.worldDataServer, this.server.getPlayerList().save());
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
public void save(@Nullable IProgressUpdate iprogressupdate, boolean flag, boolean flag1) {
|
|
ChunkProviderServer chunkproviderserver = this.getChunkProvider();
|
|
|
|
if (!flag1) {
|
|
- org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); // CraftBukkit
|
|
+ if (flag) org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); // CraftBukkit // Paper
|
|
try (co.aikar.timings.Timing ignored = timings.worldSave.startTiming()) { // Paper
|
|
if (iprogressupdate != null) {
|
|
iprogressupdate.a(new ChatMessage("menu.savingLevel"));
|
|
@@ -806,6 +838,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
|
|
// CraftBukkit end
|
|
}
|
|
|
|
+ private void saveData() { this.aj(); } // Paper - OBFHELPER
|
|
private void aj() {
|
|
if (this.dragonBattle != null) {
|
|
this.worldDataServer.a(this.dragonBattle.a()); // CraftBukkit
|