diff options
Diffstat (limited to 'patches/server/0450-incremental-chunk-and-player-saving.patch')
-rw-r--r-- | patches/server/0450-incremental-chunk-and-player-saving.patch | 375 |
1 files changed, 0 insertions, 375 deletions
diff --git a/patches/server/0450-incremental-chunk-and-player-saving.patch b/patches/server/0450-incremental-chunk-and-player-saving.patch deleted file mode 100644 index 8c38af4a69..0000000000 --- a/patches/server/0450-incremental-chunk-and-player-saving.patch +++ /dev/null @@ -1,375 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Shane Freeder <[email protected]> -Date: Sun, 9 Jun 2019 03:53:22 +0100 -Subject: [PATCH] incremental chunk and player saving - - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 7d2fee97f4d08eae245475c4b60c1a7ba46c840d..48650bc1c09b18f1b57d9828dfe27f51c74c4a75 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -854,7 +854,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - - try { - this.isSaving = true; -- this.getPlayerList().saveAll(); -+ this.getPlayerList().saveAll(); // Diff on change - flag3 = this.saveAllChunks(suppressLogs, flush, force); - } finally { - this.isSaving = false; -@@ -1400,13 +1400,28 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - } - } - -- if (this.autosavePeriod > 0 && this.tickCount % this.autosavePeriod == 0) { // CraftBukkit -- MinecraftServer.LOGGER.debug("Autosave started"); -- this.profiler.push("save"); -- this.saveEverything(true, false, false); -- this.profiler.pop(); -- MinecraftServer.LOGGER.debug("Autosave finished"); -+ // Paper start - incremental chunk and player saving -+ int playerSaveInterval = io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.rate; -+ if (playerSaveInterval < 0) { -+ playerSaveInterval = autosavePeriod; - } -+ this.profiler.push("save"); -+ final boolean fullSave = autosavePeriod > 0 && this.tickCount % autosavePeriod == 0; -+ try { -+ this.isSaving = true; -+ if (playerSaveInterval > 0) { -+ this.playerList.saveAll(playerSaveInterval); -+ } -+ for (ServerLevel level : this.getAllLevels()) { -+ if (level.paperConfig().chunks.autoSaveInterval.value() > 0) { -+ level.saveIncrementally(fullSave); -+ } -+ } -+ } finally { -+ this.isSaving = false; -+ } -+ this.profiler.pop(); -+ // Paper end - io.papermc.paper.util.CachedLists.reset(); // Paper - // Paper start - move executeAll() into full server tick timing - try (co.aikar.timings.Timing ignored = MinecraftTimings.processTasksTimer.startTiming()) { -diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index 0f75a109c06eb3113be74cf49ec560f5e2ea9cfc..ac42029596ae0c824bf33a4058ac1009740e29ea 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkHolder.java -+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java -@@ -99,6 +99,8 @@ public class ChunkHolder { - com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInMobSpawnRange; - com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInChunkTickRange; - // Paper end - optimise anyPlayerCloseEnoughForSpawning -+ long lastAutoSaveTime; // Paper - incremental autosave -+ long inactiveTimeStart; // Paper - incremental autosave - - public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) { - this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size()); -@@ -527,7 +529,19 @@ public class ChunkHolder { - boolean flag2 = playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER); - boolean flag3 = playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER); - -+ boolean prevHasBeenLoaded = this.wasAccessibleSinceLastSave; // Paper - this.wasAccessibleSinceLastSave |= flag3; -+ // Paper start - incremental autosave -+ if (this.wasAccessibleSinceLastSave & !prevHasBeenLoaded) { -+ long timeSinceAutoSave = this.inactiveTimeStart - this.lastAutoSaveTime; -+ if (timeSinceAutoSave < 0) { -+ // safest bet is to assume autosave is needed here -+ timeSinceAutoSave = this.chunkMap.level.paperConfig().chunks.autoSaveInterval.value(); -+ } -+ this.lastAutoSaveTime = this.chunkMap.level.getGameTime() - timeSinceAutoSave; -+ this.chunkMap.autoSaveQueue.add(this); -+ } -+ // Paper end - if (!flag2 && flag3) { - int expectCreateCount = ++this.fullChunkCreateCount; // Paper - this.fullChunkFuture = chunkStorage.prepareAccessibleChunk(this); -@@ -689,8 +703,32 @@ public class ChunkHolder { - } - - public void refreshAccessibility() { -+ boolean prev = this.wasAccessibleSinceLastSave; // Paper - this.wasAccessibleSinceLastSave = ChunkHolder.getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER); -+ // Paper start - incremental autosave -+ if (prev != this.wasAccessibleSinceLastSave) { -+ if (this.wasAccessibleSinceLastSave) { -+ long timeSinceAutoSave = this.inactiveTimeStart - this.lastAutoSaveTime; -+ if (timeSinceAutoSave < 0) { -+ // safest bet is to assume autosave is needed here -+ timeSinceAutoSave = this.chunkMap.level.paperConfig().chunks.autoSaveInterval.value(); -+ } -+ this.lastAutoSaveTime = this.chunkMap.level.getGameTime() - timeSinceAutoSave; -+ this.chunkMap.autoSaveQueue.add(this); -+ } else { -+ this.inactiveTimeStart = this.chunkMap.level.getGameTime(); -+ this.chunkMap.autoSaveQueue.remove(this); -+ } -+ } -+ // Paper end -+ } -+ -+ // Paper start - incremental autosave -+ public boolean setHasBeenLoaded() { -+ this.wasAccessibleSinceLastSave = getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER); -+ return this.wasAccessibleSinceLastSave; - } -+ // Paper end - - public void replaceProtoChunk(ImposterProtoChunk chunk) { - for (int i = 0; i < this.futures.length(); ++i) { -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index b0e0f85e04438affb8d8e0f75055ea83d0c03bcd..7493da0f1c3f8ab0ebc517347ef23fbe2747a306 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -103,6 +103,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp - import net.minecraft.world.level.storage.DimensionDataStorage; - import net.minecraft.world.level.storage.LevelStorageSource; - import net.minecraft.world.phys.Vec3; -+import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; // Paper - import org.apache.commons.lang3.mutable.MutableBoolean; - import org.apache.commons.lang3.mutable.MutableObject; - import org.slf4j.Logger; -@@ -779,6 +780,64 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - - } - -+ // Paper start - incremental autosave -+ final ObjectRBTreeSet<ChunkHolder> autoSaveQueue = new ObjectRBTreeSet<>((playerchunk1, playerchunk2) -> { -+ int timeCompare = Long.compare(playerchunk1.lastAutoSaveTime, playerchunk2.lastAutoSaveTime); -+ if (timeCompare != 0) { -+ return timeCompare; -+ } -+ -+ return Long.compare(MCUtil.getCoordinateKey(playerchunk1.pos), MCUtil.getCoordinateKey(playerchunk2.pos)); -+ }); -+ -+ protected void saveIncrementally() { -+ int savedThisTick = 0; -+ // optimized since we search far less chunks to hit ones that need to be saved -+ List<ChunkHolder> reschedule = new java.util.ArrayList<>(this.level.paperConfig().chunks.maxAutoSaveChunksPerTick); -+ long currentTick = this.level.getGameTime(); -+ long maxSaveTime = currentTick - this.level.paperConfig().chunks.autoSaveInterval.value(); -+ -+ for (Iterator<ChunkHolder> iterator = this.autoSaveQueue.iterator(); iterator.hasNext();) { -+ ChunkHolder playerchunk = iterator.next(); -+ if (playerchunk.lastAutoSaveTime > maxSaveTime) { -+ break; -+ } -+ -+ iterator.remove(); -+ -+ ChunkAccess ichunkaccess = playerchunk.getChunkToSave().getNow(null); -+ if (ichunkaccess instanceof LevelChunk) { -+ boolean shouldSave = ((LevelChunk)ichunkaccess).lastSaveTime <= maxSaveTime; -+ -+ if (shouldSave && this.save(ichunkaccess) && this.level.entityManager.storeChunkSections(playerchunk.pos.toLong(), entity -> {})) { -+ ++savedThisTick; -+ -+ if (!playerchunk.setHasBeenLoaded()) { -+ // do not fall through to reschedule logic -+ playerchunk.inactiveTimeStart = currentTick; -+ if (savedThisTick >= this.level.paperConfig().chunks.maxAutoSaveChunksPerTick) { -+ break; -+ } -+ continue; -+ } -+ } -+ } -+ -+ reschedule.add(playerchunk); -+ -+ if (savedThisTick >= this.level.paperConfig().chunks.maxAutoSaveChunksPerTick) { -+ break; -+ } -+ } -+ -+ for (int i = 0, len = reschedule.size(); i < len; ++i) { -+ ChunkHolder playerchunk = reschedule.get(i); -+ playerchunk.lastAutoSaveTime = this.level.getGameTime(); -+ this.autoSaveQueue.add(playerchunk); -+ } -+ } -+ // Paper end -+ - protected void saveAllChunks(boolean flush) { - // Paper start - do not overload I/O threads with too much work when saving - int[] saved = new int[1]; -@@ -874,13 +933,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - - int l = 0; -- Iterator objectiterator = net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).iterator(); // Paper -- -- while (l < 20 && shouldKeepTicking.getAsBoolean() && objectiterator.hasNext()) { -- if (this.saveChunkIfNeeded((ChunkHolder) objectiterator.next())) { -- ++l; -- } -- } -+ // Paper - incremental chunk and player saving - - } - -@@ -916,6 +969,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - - this.level.unload(chunk); - } -+ this.autoSaveQueue.remove(holder); // Paper - - this.lightEngine.updateChunkStatus(ichunkaccess.getPos()); - this.lightEngine.tryScheduleUpdate(); -@@ -1367,6 +1421,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - asyncSaveData, chunk); - - chunk.setUnsaved(false); -+ chunk.setLastSaved(this.level.getGameTime()); // Paper - track last saved time - } - // Paper end - -@@ -1376,6 +1431,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - if (!chunk.isUnsaved()) { - return false; - } else { -+ chunk.setLastSaved(this.level.getGameTime()); // Paper - track save time - chunk.setUnsaved(false); - ChunkPos chunkcoordintpair = chunk.getPos(); - -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index a59782d6f3640262c377a676e2b2ef5ec82563db..b5f46703e536f8138ff4e6769485c45b35941f9f 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -670,6 +670,15 @@ public class ServerChunkCache extends ChunkSource { - } // Paper - Timings - } - -+ // Paper start - duplicate save, but call incremental -+ public void saveIncrementally() { -+ this.runDistanceManagerUpdates(); -+ try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings -+ this.chunkMap.saveIncrementally(); -+ } // Paper - Timings -+ } -+ // Paper end -+ - @Override - public void close() throws IOException { - // CraftBukkit start -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index c9ecc7593c299b351308634db44596a76fd0c09b..8b5eac2ad96c0ebb6eae04585998cade578ff74b 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1087,6 +1087,37 @@ public class ServerLevel extends Level implements WorldGenLevel { - return !this.server.isUnderSpawnProtection(this, pos, player) && this.getWorldBorder().isWithinBounds(pos); - } - -+ // Paper start - derived from below -+ public void saveIncrementally(boolean doFull) { -+ ServerChunkCache chunkproviderserver = this.getChunkSource(); -+ -+ if (doFull) { -+ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); -+ } -+ -+ try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) { -+ if (doFull) { -+ this.saveLevelData(); -+ } -+ -+ this.timings.worldSaveChunks.startTiming(); // Paper -+ if (!this.noSave()) chunkproviderserver.saveIncrementally(); -+ this.timings.worldSaveChunks.stopTiming(); // Paper -+ -+ // Copied from save() -+ // CraftBukkit start - moved from MinecraftServer.saveChunks -+ if (doFull) { // Paper -+ ServerLevel worldserver1 = this; -+ -+ this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings()); -+ this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save()); -+ this.convertable.saveDataTag(this.server.registryHolder, this.serverLevelData, this.server.getPlayerList().getSingleplayerData()); -+ } -+ // CraftBukkit end -+ } -+ } -+ // Paper end -+ - public void save(@Nullable ProgressListener progressListener, boolean flush, boolean savingDisabled) { - ServerChunkCache chunkproviderserver = this.getChunkSource(); - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index be7d2275548936beade4aba02dc5b14fec95117a..6f2b52165c1935511790a429792d3754251537c8 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -179,6 +179,7 @@ import org.bukkit.inventory.MainHand; - public class ServerPlayer extends Player { - - private static final Logger LOGGER = LogUtils.getLogger(); -+ public long lastSave = MinecraftServer.currentTick; // Paper - private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32; - private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10; - public ServerGamePacketListenerImpl connection; -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 32ab0cd6cb42b0ab8a14f790dfcf4b155c945d6d..1850ce4566e6c5d19140cbf2636b3573f16c4239 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -569,6 +569,7 @@ public abstract class PlayerList { - protected void save(ServerPlayer player) { - if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit - if (!player.didPlayerJoinEvent) return; // Paper - If we never fired PJE, we disconnected during login. Data has not changed, and additionally, our saved vehicle is not loaded! If we save now, we will lose our vehicle (CraftBukkit bug) -+ player.lastSave = MinecraftServer.currentTick; // Paper - this.playerIo.save(player); - ServerStatsCounter serverstatisticmanager = (ServerStatsCounter) player.getStats(); // CraftBukkit - -@@ -1171,10 +1172,22 @@ public abstract class PlayerList { - } - - public void saveAll() { -+ // Paper start - incremental player saving -+ this.saveAll(-1); -+ } -+ -+ public void saveAll(int interval) { - net.minecraft.server.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main - MinecraftTimings.savePlayers.startTiming(); // Paper -+ int numSaved = 0; -+ long now = MinecraftServer.currentTick; - for (int i = 0; i < this.players.size(); ++i) { -- this.save(this.players.get(i)); -+ ServerPlayer entityplayer = this.players.get(i); -+ if (interval == -1 || now - entityplayer.lastSave >= interval) { -+ this.save(entityplayer); -+ if (interval != -1 && ++numSaved <= io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.maxPerTick()) { break; } -+ } -+ // Paper end - } - MinecraftTimings.savePlayers.stopTiming(); // Paper - return null; }); // Paper - ensure main -diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -index dc164608bfb2fb18a1adf83fa10bac4028dcac0a..a97909e77b9b28aede8c8716831c3f9a90618f09 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -@@ -457,6 +457,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom - public LevelHeightAccessor getHeightAccessorForGeneration() { - return this; - } -+ public void setLastSaved(long ticks) {} // Paper - - public static record TicksToSave(SerializableTickContainer<Block> blocks, SerializableTickContainer<Fluid> fluids) { - -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index c8444f4bfd127b7d8194aaa984505eff249ae094..2981ba61e347b8660082ff946521fc7f219d2c0d 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -87,6 +87,12 @@ public class LevelChunk extends ChunkAccess { - private final Int2ObjectMap<GameEventDispatcher> gameEventDispatcherSections; - private final LevelChunkTicks<Block> blockTicks; - private final LevelChunkTicks<Fluid> fluidTicks; -+ // Paper start - track last save time -+ public long lastSaveTime; -+ public void setLastSaved(long ticks) { -+ this.lastSaveTime = ticks; -+ } -+ // Paper end - - public LevelChunk(Level world, ChunkPos pos) { - this(world, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, (LevelChunkSection[]) null, (LevelChunk.PostLoadProcessor) null, (BlendingData) null); |