aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0450-incremental-chunk-and-player-saving.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0450-incremental-chunk-and-player-saving.patch')
-rw-r--r--patches/server/0450-incremental-chunk-and-player-saving.patch375
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);