From 8d8d26a043ca64b9af562833a593222b20caec71 Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 19 Sep 2016 23:16:39 -0400 Subject: [PATCH] Auto Save Improvements Makes Auto Save Rate setting configurable per-world. If the auto save rate is left -1, the global bukkit.yml value will be used. Process auto save every tick instead of once per auto tick interval, so that chunk saves will distribute over many ticks instead of all at once. Re-introduce a cap per tick for auto save (Spigot disabled the vanilla cap) and make it configurable. Adds incremental player auto saving too diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java index 51b0dd972..9f8b43d2a 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -269,4 +269,15 @@ public class PaperConfig { flyingKickPlayerMessage = getString("messages.kick.flying-player", flyingKickPlayerMessage); flyingKickVehicleMessage = getString("messages.kick.flying-vehicle", flyingKickVehicleMessage); } + + 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 your 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 d8f258105..47bfb5b62 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -2,6 +2,7 @@ package com.destroystokyo.paper; import java.util.List; +import net.minecraft.server.MinecraftServer; import org.bukkit.configuration.file.YamlConfiguration; import org.spigotmc.SpigotWorldConfig; @@ -315,4 +316,19 @@ public class PaperWorldConfig { private void skipEntityTickingInChunksScheduledForUnload() { skipEntityTickingInChunksScheduledForUnload = getBoolean("skip-entity-ticking-in-chunks-scheduled-for-unload", skipEntityTickingInChunksScheduledForUnload); } + + 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 = 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 ddccdc939..0d70ce797 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -53,9 +53,9 @@ public class Chunk implements IChunkAccess { private final TickList s; private final TickList t; private boolean u; - private boolean v; + private boolean v;public boolean hasEntities() { return v; } // Paper - OBFHELPER private long lastSaved; - private boolean x; + private boolean x; public boolean isModified() { return x; } // Paper - OBFHELPER private int y; private long z; private int A; @@ -1071,11 +1071,11 @@ public class Chunk implements IChunkAccess { if (this.v && this.world.getTime() != this.lastSaved || this.x) { return true; } - } else if (this.v && this.world.getTime() >= this.lastSaved + MinecraftServer.getServer().autosavePeriod * 4) { // Spigot - Only save if we've passed 2 auto save intervals without modification - return true; } - - return this.x; + // Paper start - Make world configurable and incremental + // This !flag section should say if isModified or hasEntities, then check auto save + return ((isModified() || hasEntities()) && this.world.getTime() >= this.lastSaved + world.paperConfig.autoSavePeriod); + // Paper end } public boolean isEmpty() { diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java index ff6f7596c..91533f8ac 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -240,7 +240,7 @@ public class ChunkProviderServer implements IChunkProvider { this.saveChunk(chunk, false); // Spigot chunk.a(false); ++i; - if (i == 24 && !flag && false) { // Spigot + if (!flag && i >= world.paperConfig.maxAutoSaveChunksPerTick) { // Spigot - // Paper - Incremental Auto Save - cap max return false; } } diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java index 749e41aed..97714be6d 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -34,6 +34,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { private static final Logger cc = LogManager.getLogger(); public String locale = null; // CraftBukkit - lowercase // Paper - default to null + public long lastSave = MinecraftServer.currentTick; // Paper public PlayerConnection playerConnection; public final MinecraftServer server; public final PlayerInteractManager playerInteractManager; diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 7710962ce..f09d358e2 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -143,6 +143,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati public org.bukkit.command.RemoteConsoleCommandSender remoteConsole; public ConsoleReader reader; public static int currentTick = 0; // Paper - Further improve tick loop + public boolean serverAutoSave = false; // Paper public final Thread primaryThread; public java.util.Queue processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); public int autosavePeriod; @@ -937,22 +938,30 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati this.m.b().a(agameprofile); } - if (autosavePeriod > 0 && this.ticks % autosavePeriod == 0) { // CraftBukkit this.methodProfiler.enter("save"); - this.playerList.savePlayers(); + + serverAutoSave = (autosavePeriod > 0 && this.ticks % autosavePeriod == 0); // Paper + int playerSaveInterval = com.destroystokyo.paper.PaperConfig.playerAutoSaveRate; + if (playerSaveInterval < 0) { + playerSaveInterval = autosavePeriod; + } + if (playerSaveInterval > 0) { // CraftBukkit // Paper + this.playerList.savePlayers(playerSaveInterval); // Spigot Start + } // Paper - Incremental Auto Saving + // We replace this with saving each individual world as this.saveChunks(...) is broken, // and causes the main thread to sleep for random amounts of time depending on chunk activity // Also pass flag to only save modified chunks server.playerCommandState = true; for (World world : getWorlds()) { - world.getWorld().save(false); + if (world.paperConfig.autoSavePeriod > 0) world.getWorld().save(false); // Paper - Incremental / Configurable Auto Saving } server.playerCommandState = false; // this.saveChunks(true); // Spigot End this.methodProfiler.exit(); - } + //} // Paper - Incremental Auto Saving this.methodProfiler.enter("snooper"); if (getSnooperEnabled() && !this.snooper.d() && this.ticks > 100) { // Spigot diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java index 86aa0c57a..2c3ed3723 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -341,6 +341,7 @@ public abstract class PlayerList { protected void savePlayerFile(EntityPlayer entityplayer) { if (!entityplayer.getBukkitEntity().isPersistent()) return; // CraftBukkit + entityplayer.lastSave = MinecraftServer.currentTick; // Paper this.playerFileData.save(entityplayer); ServerStatisticManager serverstatisticmanager = (ServerStatisticManager) entityplayer.getStatisticManager(); // CraftBukkit @@ -1207,13 +1208,25 @@ public abstract class PlayerList { } + // Paper start public void savePlayers() { + savePlayers(null); + } + + public void savePlayers(Integer interval) { + long now = MinecraftServer.currentTick; MinecraftTimings.savePlayers.startTiming(); // Paper + int numSaved = 0; // Paper for (int i = 0; i < this.players.size(); ++i) { - this.savePlayerFile((EntityPlayer) this.players.get(i)); + EntityPlayer entityplayer = this.players.get(i); + if (interval == null || now - entityplayer.lastSave >= interval) { + this.savePlayerFile(entityplayer); + if (interval != null && ++numSaved <= com.destroystokyo.paper.PaperConfig.maxPlayerAutoSavePerTick) { break; } // Paper + } } MinecraftTimings.savePlayers.stopTiming(); // Paper } + // Paper end public WhiteList getWhitelist() { return this.whitelist; diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java index fa7466d64..48d70624c 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -856,8 +856,9 @@ public class WorldServer extends World implements IAsyncTaskHandler { ChunkProviderServer chunkproviderserver = this.getChunkProvider(); if (chunkproviderserver.d()) { - 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 - Incremental Auto Saving - Only fire event on full save timings.worldSave.startTiming(); // Paper + if (flag || server.serverAutoSave) { // Paper if (iprogressupdate != null) { iprogressupdate.a(new ChatMessage("menu.savingLevel", new Object[0])); } @@ -866,6 +867,7 @@ public class WorldServer extends World implements IAsyncTaskHandler { if (iprogressupdate != null) { iprogressupdate.c(new ChatMessage("menu.savingChunks", new Object[0])); } + } // Paper timings.worldSaveChunks.startTiming(); // Paper chunkproviderserver.a(flag); -- 2.20.1