Add back incremental chunk saving patch

This commit is contained in:
Jake Potrebic 2021-06-13 22:34:40 -07:00 committed by Nassim Jahnke
parent 956fcad4ea
commit 4b2f4cbebb
1 changed files with 101 additions and 84 deletions

View File

@ -2,17 +2,16 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com> From: Shane Freeder <theboyetronic@gmail.com>
Date: Sun, 9 Jun 2019 03:53:22 +0100 Date: Sun, 9 Jun 2019 03:53:22 +0100
Subject: [PATCH] incremental chunk saving Subject: [PATCH] incremental chunk saving
1.17: saved for MM
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index ffe9b1a63d78925e1d77b9e730aef42fed6d58fa..1278d09f70c1e97607ef20d87a178dc252c7f723 100644 index 3bc6329d3ea48966cb99e792f9b35e2d2d71a34b..ec51a9e19670888584e5324a57a47aa9c676d423 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -446,4 +446,19 @@ public class PaperWorldConfig { @@ -47,6 +47,21 @@ public class PaperWorldConfig {
keepLoadedRange = (short) (getInt("keep-spawn-loaded-range", Math.min(spigotConfig.viewDistance, 10)) * 16);
log( "Keep Spawn Loaded Range: " + (keepLoadedRange/16)); log( "Keep Spawn Loaded Range: " + (keepLoadedRange/16));
} }
+
+ public int autoSavePeriod = -1; + public int autoSavePeriod = -1;
+ private void autoSavePeriod() { + private void autoSavePeriod() {
+ autoSavePeriod = getInt("auto-save-interval", -1); + autoSavePeriod = getInt("auto-save-interval", -1);
@ -27,64 +26,65 @@ index ffe9b1a63d78925e1d77b9e730aef42fed6d58fa..1278d09f70c1e97607ef20d87a178dc2
+ private void maxAutoSaveChunksPerTick() { + private void maxAutoSaveChunksPerTick() {
+ maxAutoSaveChunksPerTick = getInt("max-auto-save-chunks-per-tick", 24); + maxAutoSaveChunksPerTick = getInt("max-auto-save-chunks-per-tick", 24);
+ } + }
} +
private boolean getBoolean(String path, boolean def) {
config.addDefault("world-settings.default." + path, def);
return config.getBoolean("world-settings." + worldName + "." + path, config.getBoolean("world-settings.default." + path));
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 0efe7024493f96bb54e7d8c1ea7b233a1b481a04..aab1a055c065d1f1a92461e4442ec2cdd8e0b347 100644 index 11dbe48c8a8c29cd28d725c43505e326a6e626ff..363dcebb3b2d5a2512776a191f6716ed3d0e8aff 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java --- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -261,6 +261,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa @@ -300,6 +300,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public static int currentTick = 0; // Paper - Further improve tick loop public static int currentTick = 0; // Paper - Further improve tick loop
public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>(); public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
public int autosavePeriod; public int autosavePeriod;
+ public boolean serverAutoSave = false; // Paper + public boolean serverAutoSave = false; // Paper
public Commands vanillaCommandDispatcher; public Commands vanillaCommandDispatcher;
private boolean forceTicks; public boolean forceTicks; // Paper
// CraftBukkit end // CraftBukkit end
@@ -1256,14 +1257,24 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa @@ -1411,14 +1412,23 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.status.getPlayers().setSample(agameprofile); this.status.getPlayers().setSample(agameprofile);
} }
- if (autosavePeriod > 0 && this.tickCount % autosavePeriod == 0) { // CraftBukkit - if (this.autosavePeriod > 0 && this.tickCount % this.autosavePeriod == 0) { // CraftBukkit
- MinecraftServer.LOGGER.debug("Autosave started"); - MinecraftServer.LOGGER.debug("Autosave started");
+ //if (autosavePeriod > 0 && this.ticks % autosavePeriod == 0) { // CraftBukkit // Paper - move down + // if (this.autosavePeriod > 0 && this.tickCount % this.autosavePeriod == 0) { // CraftBukkit // Paper - move down
+ //MinecraftServer.LOGGER.debug("Autosave started"); // Paper + // MinecraftServer.LOGGER.debug("Autosave started"); // Paper
+ serverAutoSave = (autosavePeriod > 0 && this.tickCount % autosavePeriod == 0); // Paper + serverAutoSave = (autosavePeriod > 0 && this.tickCount % autosavePeriod == 0); // Paper
this.profiler.push("save"); this.profiler.push("save");
+ if (autosavePeriod > 0 && this.tickCount % autosavePeriod == 0) { // Paper + if (this.autosavePeriod > 0 && this.tickCount % this.autosavePeriod == 0) { // Paper - moved from above
this.playerList.saveAll(); this.playerList.saveAll();
- this.saveAllChunks(true, false, false); - this.saveAllChunks(true, false, false);
+ }// Paper - this.profiler.pop();
+ // Paper start
+ for (ServerLevel world : getAllLevels()) {
+ if (world.paperConfig.autoSavePeriod > 0) {
+ world.saveIncrementally(serverAutoSave);
+ }
+ }
+ // Paper end
+
this.profiler.pop();
- MinecraftServer.LOGGER.debug("Autosave finished"); - MinecraftServer.LOGGER.debug("Autosave finished");
- } + // this.saveAllChunks(true, false, false); // Paper - saved incrementally below
+ //MinecraftServer.LOGGER.debug("Autosave finished"); // Paper + } // Paper start
+ for (ServerLevel level : this.getAllLevels()) {
+ if (level.paperConfig.autoSavePeriod > 0) {
+ level.saveIncrementally(this.serverAutoSave);
+ }
}
+ // Paper end
+ this.profiler.pop();
+ // MinecraftServer.LOGGER.debug("Autosave finished"); // Paper
+ //} // Paper + //} // Paper
this.profiler.push("snooper"); this.profiler.push("snooper");
if (((DedicatedServer) this).getProperties().snooperEnabled && !this.snooper.isStarted() && this.tickCount > 100) { // Spigot if (((DedicatedServer) this).getProperties().snooperEnabled && !this.snooper.isStarted() && this.tickCount > 100) { // Spigot
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index 7010e0a970462d2b2e1b5696a1a49dba9ea60935..491a9e78fdcec8c211499e8f48cceb829f1e5c8b 100644 index 969b0c9cf6d7eb2055d3b804f25a3cbc161ceaea..1f67c9c5f7161ea687983e7ae0ec7d259da9acd3 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -66,6 +66,9 @@ public class ChunkHolder { @@ -111,6 +111,8 @@ public class ChunkHolder {
this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
private final ChunkMap chunkMap; // Paper }
// Paper end - optimise isOutsideOfRange
+ long lastAutoSaveTime; // Paper - incremental autosave + long lastAutoSaveTime; // Paper - incremental autosave
+ long inactiveTimeStart; // Paper - incremental autosave + long inactiveTimeStart; // Paper - incremental autosave
+
public ChunkHolder(ChunkPos pos, int level, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) { public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) {
this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size()); this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size());
this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; @@ -533,7 +535,19 @@ public class ChunkHolder {
@@ -421,7 +424,19 @@ public class ChunkHolder {
boolean flag2 = playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER); boolean flag2 = playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
boolean flag3 = playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER); boolean flag3 = playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
@ -102,14 +102,14 @@ index 7010e0a970462d2b2e1b5696a1a49dba9ea60935..491a9e78fdcec8c211499e8f48cceb82
+ } + }
+ // Paper end + // Paper end
if (!flag2 && flag3) { if (!flag2 && flag3) {
// Paper start - cache ticking ready status int expectCreateCount = ++this.fullChunkCreateCount; // Paper
int expectCreateCount = ++this.fullChunkCreateCount; this.fullChunkFuture = chunkStorage.prepareAccessibleChunk(this);
@@ -541,8 +556,32 @@ public class ChunkHolder { @@ -654,9 +668,33 @@ public class ChunkHolder {
} }
public void refreshAccessibility() { public void refreshAccessibility() {
+ boolean prev = this.wasAccessibleSinceLastSave; // Paper + boolean prev = this.wasAccessibleSinceLastSave; // Paper
+ this.wasAccessibleSinceLastSave = getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER); this.wasAccessibleSinceLastSave = ChunkHolder.getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
+ // Paper start - incremental autosave + // Paper start - incremental autosave
+ if (prev != this.wasAccessibleSinceLastSave) { + if (prev != this.wasAccessibleSinceLastSave) {
+ if (this.wasAccessibleSinceLastSave) { + if (this.wasAccessibleSinceLastSave) {
@ -126,22 +126,23 @@ index 7010e0a970462d2b2e1b5696a1a49dba9ea60935..491a9e78fdcec8c211499e8f48cceb82
+ } + }
+ } + }
+ // Paper end + // Paper end
+ } }
+
+ // Paper start - incremental autosave + // Paper start - incremental autosave
+ public boolean setHasBeenLoaded() { + public boolean setHasBeenLoaded() {
this.wasAccessibleSinceLastSave = getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER); + this.wasAccessibleSinceLastSave = getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
+ return this.wasAccessibleSinceLastSave; + return this.wasAccessibleSinceLastSave;
} + }
+ // Paper end + // Paper end
+
public void replaceProtoChunk(ImposterProtoChunk protochunkextension) { public void replaceProtoChunk(ImposterProtoChunk chunk) {
for (int i = 0; i < this.futures.length(); ++i) { for (int i = 0; i < this.futures.length(); ++i) {
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = (CompletableFuture) this.futures.get(i);
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0aac29de933c84c34cb24e204e8fcc7010060d8f..cfec04e12dfaeb8852dc129a6a7e68c61dac54b6 100644 index d8f99f7f5ca0e1dbbb9b760af3a4b4f9c52ef6c7..5a5e9188f55405c8a2646891c348d544d33eb940 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java --- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -91,6 +91,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana @@ -93,6 +93,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana
import net.minecraft.world.level.storage.DimensionDataStorage; import net.minecraft.world.level.storage.DimensionDataStorage;
import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@ -149,7 +150,7 @@ index 0aac29de933c84c34cb24e204e8fcc7010060d8f..cfec04e12dfaeb8852dc129a6a7e68c6
import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -378,6 +379,64 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -744,6 +745,64 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} }
@ -212,30 +213,37 @@ index 0aac29de933c84c34cb24e204e8fcc7010060d8f..cfec04e12dfaeb8852dc129a6a7e68c6
+ // Paper end + // Paper end
+ +
protected void saveAllChunks(boolean flush) { protected void saveAllChunks(boolean flush) {
Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunks = this.getVisibleChunks(); // Paper remove clone of visible Chunks unless saving off main thread (watchdog kill)
if (flush) { if (flush) {
List<ChunkHolder> list = (List) this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); @@ -883,6 +942,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -488,6 +547,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
@@ -905,6 +965,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.level.unload(chunk); this.level.unload(chunk);
} }
+ this.autoSaveQueue.remove(playerchunk); // Paper + this.autoSaveQueue.remove(holder); // Paper
this.lightEngine.updateChunkStatus(ichunkaccess.getPos()); // Paper start - async chunk saving
this.lightEngine.tryScheduleUpdate(); try {
@@ -680,6 +740,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -1231,6 +1292,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
playerchunk.replaceProtoChunk(new ImposterProtoChunk(chunk)); if (!chunk.isUnsaved()) {
} return false;
} else {
+ chunk.setLastSaved(this.level.getGameTime()); // Paper - track save time
chunk.setUnsaved(false);
ChunkPos chunkcoordintpair = chunk.getPos();
+ chunk.setLastSaveTime(this.level.getGameTime() - 1); // Paper - avoid autosaving newly generated/loaded chunks
+
chunk.setFullStatus(() -> {
return ChunkHolder.getFullChunkStatus(playerchunk.getTicketLevel());
});
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 1e8ac0110badbf2d1c2336168c3e11991667c782..c1aa40c01a80a8870478193b8cd7354b0d71045c 100644 index 3faa808f41f057a9956c697ec1323330f5920b86..7ab28e9bd3f785838b7fa4ac5811c0e71cddcb61 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -558,6 +558,15 @@ public class ServerChunkCache extends ChunkSource { @@ -671,6 +671,15 @@ public class ServerChunkCache extends ChunkSource {
} // Paper - Timings } // Paper - Timings
} }
@ -252,10 +260,10 @@ index 1e8ac0110badbf2d1c2336168c3e11991667c782..c1aa40c01a80a8870478193b8cd7354b
public void close() throws IOException { public void close() throws IOException {
// CraftBukkit start // CraftBukkit start
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index b2ddf145ae9f581ec6820deb9cb6a98be87658d7..fd7ee4badb383ffb4347d62c00ea2dfa3d76fd12 100644 index fd3159f7767faaa55ed49eba237e30a2dbd4fa92..9fb7345a8c3985aa3e0f4575d680b82379d2cc5a 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java --- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -882,6 +882,38 @@ public class ServerLevel extends net.minecraft.world.level.Level implements Worl @@ -1014,6 +1014,38 @@ public class ServerLevel extends net.minecraft.world.level.Level implements Worl
return !this.server.isUnderSpawnProtection(this, pos, player) && this.getWorldBorder().isWithinBounds(pos); return !this.server.isUnderSpawnProtection(this, pos, player) && this.getWorldBorder().isWithinBounds(pos);
} }
@ -267,14 +275,14 @@ index b2ddf145ae9f581ec6820deb9cb6a98be87658d7..fd7ee4badb383ffb4347d62c00ea2dfa
+ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); + org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld()));
+ } + }
+ +
+ try (co.aikar.timings.Timing ignored = timings.worldSave.startTiming()) { + try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) {
+ if (doFull) { + if (doFull) {
+ this.saveData(); + this.saveLevelData();
+ } + }
+ +
+ timings.worldSaveChunks.startTiming(); // Paper + this.timings.worldSaveChunks.startTiming(); // Paper
+ if (!this.noSave()) chunkproviderserver.saveIncrementally(); + if (!this.noSave()) chunkproviderserver.saveIncrementally();
+ timings.worldSaveChunks.stopTiming(); // Paper + this.timings.worldSaveChunks.stopTiming(); // Paper
+ +
+ +
+ // Copied from save() + // Copied from save()
@ -282,9 +290,9 @@ index b2ddf145ae9f581ec6820deb9cb6a98be87658d7..fd7ee4badb383ffb4347d62c00ea2dfa
+ if (doFull) { // Paper + if (doFull) { // Paper
+ ServerLevel worldserver1 = this; + ServerLevel worldserver1 = this;
+ +
+ worldDataServer.setWorldBorder(worldserver1.getWorldBorder().createSettings()); + this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings());
+ worldDataServer.setCustomBossEvents(this.server.getCustomBossEvents().save()); + this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save());
+ convertable.saveDataTag(this.server.registryHolder, this.worldDataServer, this.server.getPlayerList().getSingleplayerData()); + this.convertable.saveDataTag(this.server.registryHolder, this.serverLevelData, this.server.getPlayerList().getSingleplayerData());
+ } + }
+ // CraftBukkit end + // CraftBukkit end
+ } + }
@ -294,24 +302,33 @@ index b2ddf145ae9f581ec6820deb9cb6a98be87658d7..fd7ee4badb383ffb4347d62c00ea2dfa
public void save(@Nullable ProgressListener progressListener, boolean flush, boolean flag1) { public void save(@Nullable ProgressListener progressListener, boolean flush, boolean flag1) {
ServerChunkCache chunkproviderserver = this.getChunkSource(); ServerChunkCache chunkproviderserver = this.getChunkSource();
@@ -912,6 +944,7 @@ public class ServerLevel extends net.minecraft.world.level.Level implements Worl diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
// CraftBukkit end index c0075d226331f32e470dae5bf1ce8d79e8b263dc..8ba782511b0a6c7859cbcf910ad742cbb9f599e5 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -29,6 +29,7 @@ public interface ChunkAccess extends BlockGetter, FeatureAccess {
return GameEventDispatcher.NOOP;
} }
+ private void saveData() { this.saveLevelData(); } // Paper - OBFHELPER + default void setLastSaved(long ticks) {}
private void saveLevelData() { // Paper start
if (this.dragonFight != null) { default boolean generateFlatBedrock() {
this.worldDataServer.setEndDragonFightData(this.dragonFight.saveData()); // CraftBukkit if (this instanceof ProtoChunk) {
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java 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 55872a17060a35b727a597bc414fecec3ada3515..419b4bf0549d798d52d73fbbd9de59313fc05eb1 100644 index a63dc77db41dab79f03ef7384da55c1cdeca5d98..efb9c6fef915b43c9dd4468ead52aa36ea9e7ef3 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -80,7 +80,7 @@ public class LevelChunk implements ChunkAccess { @@ -108,6 +108,13 @@ public class LevelChunk implements ChunkAccess {
private final ShortList[] postProcessing;
private TickList<Block> blockTicks; private TickList<Block> blockTicks;
private TickList<Fluid> liquidTicks; private TickList<Fluid> liquidTicks;
private boolean lastSaveHadEntities; + // Paper start - track last save time
- private long lastSaveTime; + public long lastSaveTime;
+ public long lastSaveTime; // Paper + @Override
+ public void setLastSaved(long ticks) {
+ this.lastSaveTime = ticks;
+ }
+ // Paper end
private volatile boolean unsaved; private volatile boolean unsaved;
private long inhabitedTime; private long inhabitedTime;
@Nullable @Nullable