From 50a06f81e5749b70315ab59c6e1995f3d64284d5 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 7018828..dcb971a 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -241,4 +241,9 @@ public class PaperConfig { flyingKickPlayerMessage = getString("messages.kick.flying-player", flyingKickPlayerMessage); flyingKickVehicleMessage = getString("messages.kick.flying-vehicle", flyingKickVehicleMessage); } + + public static int playerAutoSaveRate = -1; + private static void playerAutoSaveRate() { + playerAutoSaveRate = getInt("settings.player-auto-save-rate", -1); + } } diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java index 965edc9..da530f9 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.Bukkit; import org.bukkit.configuration.file.YamlConfiguration; import org.spigotmc.SpigotWorldConfig; @@ -365,4 +366,19 @@ public class PaperWorldConfig { private void elytraHitWallDamage() { elytraHitWallDamage = getBoolean("elytra-hit-wall-damage", true); } + + 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 88437d7..9f7f32d 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -960,11 +960,9 @@ public class Chunk { if (this.t && this.world.getTime() != this.lastSaved || this.s) { return true; } - } else if (this.t && 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.s; + // This !flag section should say if s(isModified) or t(hasEntities), then check auto save + return ((this.s || this.t) && this.world.getTime() >= this.lastSaved + world.paperConfig.autoSavePeriod); // Paper - Make world configurable and incremental } public Random a(long i) { diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java index 7a56a64..5c5a56c 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -1,5 +1,6 @@ package net.minecraft.server; +import com.destroystokyo.paper.PaperConfig; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; @@ -266,7 +267,7 @@ public class ChunkProviderServer implements IChunkProvider { this.saveChunk(chunk); chunk.f(false); ++i; - if (i == 24 && !flag && false) { // Spigot + if (!flag && i >= world.paperConfig.maxAutoSaveChunksPerTick) { // Spigot - // Paper - Incremental Auto Save - cap max per tick return false; } } diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java index de2d439..4ad336e 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -29,6 +29,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { private static final Logger bR = LogManager.getLogger(); public String locale = null; // Spigot private -> public // 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 b3e0ef7..9cdcdc4 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -117,6 +117,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs public final Thread primaryThread; public java.util.Queue processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); public int autosavePeriod; + public boolean serverAutoSave = false; // Paper // CraftBukkit end public MinecraftServer(OptionSet options, Proxy proxy, DataConverterManager dataconvertermanager, YggdrasilAuthenticationService yggdrasilauthenticationservice, MinecraftSessionService minecraftsessionservice, GameProfileRepository gameprofilerepository, UserCache usercache) { @@ -760,22 +761,30 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs this.q.b().a(agameprofile); } - if (autosavePeriod > 0 && this.ticks % autosavePeriod == 0) { // CraftBukkit this.methodProfiler.a("save"); - this.v.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.v.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 : worlds) { - 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.b(); - } + //} // Paper - Incremental Auto Saving this.methodProfiler.a("tallying"); this.h[this.ticks % 100] = System.nanoTime() - i; diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java index b14d4cb..2f527f6 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -330,6 +330,7 @@ public abstract class PlayerList { } protected void savePlayerFile(EntityPlayer entityplayer) { + entityplayer.lastSave = MinecraftServer.currentTick; // Paper this.playerFileData.save(entityplayer); ServerStatisticManager serverstatisticmanager = (ServerStatisticManager) this.o.get(entityplayer.getUniqueID()); @@ -1205,13 +1206,23 @@ public abstract class PlayerList { } + // Paper start public void savePlayers() { + savePlayers(null); + } + + public void savePlayers(Integer interval) { + long now = MinecraftServer.currentTick; MinecraftTimings.savePlayers.startTiming(); // 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); + } } MinecraftTimings.savePlayers.stopTiming(); // Paper } + // Paper end public void addWhitelist(GameProfile gameprofile) { this.whitelist.add(new WhiteListEntry(gameprofile)); diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java index 5bf7df7..bfa87d7 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -1002,8 +1002,9 @@ public class WorldServer extends World implements IAsyncTaskHandler { ChunkProviderServer chunkproviderserver = this.getChunkProviderServer(); if (chunkproviderserver.e()) { - 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("Saving level"); } @@ -1012,6 +1013,7 @@ public class WorldServer extends World implements IAsyncTaskHandler { if (iprogressupdate != null) { iprogressupdate.c("Saving chunks"); } + } // Paper timings.worldSaveChunks.startTiming(); // Paper chunkproviderserver.a(flag); -- 2.9.3