mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-02 21:17:00 +01:00
bc0dd0df3d
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: 716b4fce Revert SnakeYAML upgrade ca6f8942 Update to Minecraft 1.18-rc3 57e7e952 #683: Add Player#showDemoScreen CraftBukkit Changes: c98abfb0 Update to Minecraft 1.18-rc3 9b258501 #960: Add Player#showDemoScreen d9542247 Produce remapped jars after bootstrap jar 99f3ddde SPIGOT-6808: Fix RegionAccessor#getBiome Spigot Changes: b7a4222e Update to Minecraft 1.18-rc3
414 lines
21 KiB
Diff
414 lines
21 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/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
|
index ebbbffd209c6796bc608992e293035141a122d1f..4fb6b2153117f54a2b0ca940de4c0ee2fa85e20e 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
|
@@ -458,4 +458,14 @@ public class PaperConfig {
|
|
set("settings.unsupported-settings.allow-tnt-duplication", null);
|
|
}
|
|
|
|
+ public static int playerAutoSaveRate = -1;
|
|
+ public static int maxPlayerAutoSavePerTick = 10;
|
|
+ private static void playerAutoSaveRate() {
|
|
+ playerAutoSaveRate = getInt("settings.player-auto-save-rate", -1);
|
|
+ maxPlayerAutoSavePerTick = getInt("settings.max-player-auto-save-per-tick", -1);
|
|
+ if (maxPlayerAutoSavePerTick == -1) { // -1 Automatic / "Recommended"
|
|
+ // 10 should be safe for everyone unless you mass spamming player auto save
|
|
+ maxPlayerAutoSavePerTick = (playerAutoSaveRate == -1 || playerAutoSaveRate > 100) ? 10 : 20;
|
|
+ }
|
|
+ }
|
|
}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
index 845f105457d659a4bd88d4a8ce91b20d6abb7865..bf0c6e1b2b1934bc3fa5c17c414b3730c5ac0833 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
@@ -62,6 +62,21 @@ public class PaperWorldConfig {
|
|
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);
|
|
+ }
|
|
+
|
|
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
|
|
index 4351dd9c293d1db48c87c5df28bcb2b9a59583f7..8a3583bcf2d2a694b30718062bc7f2e1158c9b4f 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -901,7 +901,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;
|
|
@@ -1408,13 +1408,27 @@ 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 = com.destroystokyo.paper.PaperConfig.playerAutoSaveRate;
|
|
+ if (playerSaveInterval < 0) {
|
|
+ playerSaveInterval = autosavePeriod;
|
|
}
|
|
+ this.profiler.push("save");
|
|
+ try {
|
|
+ this.isSaving = true;
|
|
+ if (playerSaveInterval > 0) {
|
|
+ this.playerList.saveAll(playerSaveInterval);
|
|
+ }
|
|
+ for (ServerLevel level : this.getAllLevels()) {
|
|
+ if (level.paperConfig.autoSavePeriod > 0) {
|
|
+ level.saveIncrementally(autosavePeriod > 0 && this.tickCount % autosavePeriod == 0);
|
|
+ }
|
|
+ }
|
|
+ } 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 7c3eaa72263708f5b2cea455b3eea230bc633c5d..9c58274f680c1f873b7832d2175d1d449544bff1 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
@@ -84,6 +84,8 @@ public class ChunkHolder {
|
|
this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
|
|
}
|
|
// Paper end - optimise isOutsideOfRange
|
|
+ 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());
|
|
@@ -473,7 +475,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.autoSavePeriod;
|
|
+ }
|
|
+ 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);
|
|
@@ -604,9 +618,33 @@ 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.autoSavePeriod;
|
|
+ }
|
|
+ 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) {
|
|
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
|
|
index c9a10f327f971e68bf5bb4ed0793a046233e3d74..a65f3f24fe00b130520ba9af178f1cf7ad3ece26 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -101,6 +101,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana
|
|
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.apache.logging.log4j.LogManager;
|
|
@@ -677,6 +678,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.maxAutoSaveChunksPerTick);
|
|
+ long currentTick = this.level.getGameTime();
|
|
+ long maxSaveTime = currentTick - this.level.paperConfig.autoSavePeriod;
|
|
+
|
|
+ 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.maxAutoSaveChunksPerTick) {
|
|
+ break;
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ reschedule.add(playerchunk);
|
|
+
|
|
+ if (savedThisTick >= this.level.paperConfig.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) {
|
|
if (flush) {
|
|
List<ChunkHolder> list = (List) this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList());
|
|
@@ -772,7 +831,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
int l = 0;
|
|
ObjectIterator objectiterator = this.visibleChunkMap.values().iterator();
|
|
|
|
- while (l < 20 && shouldKeepTicking.getAsBoolean() && objectiterator.hasNext()) {
|
|
+ while (false && l < 20 && shouldKeepTicking.getAsBoolean() && objectiterator.hasNext()) { // Paper - incremental chunk and player saving
|
|
if (this.saveChunkIfNeeded((ChunkHolder) objectiterator.next())) {
|
|
++l;
|
|
}
|
|
@@ -814,6 +873,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();
|
|
@@ -1211,6 +1271,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
|
|
|
|
@@ -1220,6 +1281,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 c3875acfad91c50a7fec4b1f8e06e8a32e2ea037..3f9366019d19f8fb5f636f1b301c2824a54865cd 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -825,6 +825,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 a5f0fe87bd5d75bf9a12ef284b0d635d8f6dd3a1..d55d2627b7e0c847250c551989d438280bef9ab8 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -1055,6 +1055,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 2f13055a39c26fe12d2c1094103186635e536166..4b83617a81db1749faaf49fc3ee77e44846dce1a 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
@@ -171,6 +171,7 @@ public class ServerPlayer extends Player {
|
|
public final int getViewDistance() { return this.getLevel().getChunkSource().chunkMap.viewDistance - 1; } // Paper - placeholder
|
|
|
|
private static final Logger LOGGER = LogManager.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 30fa9ac91be2ecc55efeee9222835cbedc53acff..a40ca089d92df8f3ea0a444e3293531e787b196b 100644
|
|
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
@@ -556,6 +556,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
|
|
|
|
@@ -1158,10 +1159,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 <= com.destroystokyo.paper.PaperConfig.maxPlayerAutoSavePerTick) { 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 04b58d6aec04cc98777f88c2bcc9a820c5fd2f57..a44901edc49259ebbbb4367b0d10b8daa76b07cd 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
|
|
@@ -451,6 +451,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
|
|
public LevelHeightAccessor getHeightAccessorForGeneration() {
|
|
return this;
|
|
}
|
|
+ public void setLastSaved(long ticks) {} // Paper
|
|
|
|
// CraftBukkit start - decompile error
|
|
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 b92da719a5d35a60a2e13ccb0f55c41b242f9b50..6a881c2abdbc23366a1b0ac69e363b196b99197b 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);
|