CavePVP-Stuff/cSpigot-master/spigot-server-Patches/0135-Distribute-autosave-am...

505 lines
19 KiB
Diff

From 1de60dda62bc84a8d2e00e038deceb2fa132195e Mon Sep 17 00:00:00 2001
From: Poweruser <poweruser.rs@hotmail.com>
Date: Wed, 25 May 2016 06:10:55 +0200
Subject: [PATCH] Distribute autosave among multiple ticks
diff --git a/src/main/java/net/frozenorb/autosave/AutoSave.java b/src/main/java/net/frozenorb/autosave/AutoSave.java
new file mode 100644
index 000000000..216aa1287
--- /dev/null
+++ b/src/main/java/net/frozenorb/autosave/AutoSave.java
@@ -0,0 +1,197 @@
+package net.frozenorb.autosave;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Queue;
+
+import org.bukkit.event.world.WorldSaveEvent;
+import org.spigotmc.SpigotConfig;
+
+import net.minecraft.server.ExceptionWorldConflict;
+import net.minecraft.server.FileIOThread;
+import net.minecraft.server.IProgressUpdate;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.RegionFileCache;
+import net.minecraft.server.WorldServer;
+
+public class AutoSave {
+
+ private AutoSaveStep step;
+ private Queue<WorldServer> levelAndMapsQueue;
+ private Queue<WorldServer> saveChunksQueue;
+ private Queue<WorldServer> unloadChunksQueue;
+ private Queue<WorldServer> eventQueue;
+ private long fileioStart;
+ private long fileioEnd;
+ private int regionFileCount;
+ private long regionFileCacheStart;
+ private long regionFileCacheEnd;
+ private int chunkQueuedCount;
+
+ public AutoSave() {
+ this.levelAndMapsQueue = new ArrayDeque<WorldServer>();
+ this.saveChunksQueue = new ArrayDeque<WorldServer>();
+ this.unloadChunksQueue = new ArrayDeque<WorldServer>();
+ this.eventQueue = new ArrayDeque<WorldServer>();
+ this.reset();
+ }
+
+ public void queueWorld(WorldServer worldserver) {
+ this.levelAndMapsQueue.add(worldserver);
+ this.saveChunksQueue.add(worldserver);
+ this.unloadChunksQueue.add(worldserver);
+ this.eventQueue.add(worldserver);
+ }
+
+ public void reset() {
+ this.levelAndMapsQueue.clear();
+ this.saveChunksQueue.clear();
+ this.unloadChunksQueue.clear();
+ this.eventQueue.clear();
+ this.step = AutoSaveStep.IDLE;
+ this.chunkQueuedCount = 0;
+ }
+
+ private void moveToNextStep() {
+ this.step = AutoSaveStep.nextStep(this.step);
+ }
+
+ public boolean execute() throws ExceptionWorldConflict {
+ WorldServer worldServer;
+ switch(this.step) {
+ default:
+ case IDLE:
+ break;
+ case START:
+ MinecraftServer.getLogger().info("[Autosave] Started ..");
+ for(WorldServer world: this.saveChunksQueue) {
+ world.getAutoSaveWorldData().setLastAutosaveTimeStamp();
+ }
+ this.moveToNextStep();
+ return this.execute();
+ case SAVE_PLAYERS:
+ MinecraftServer.getServer().getPlayerList().savePlayers();
+ this.moveToNextStep();
+ break;
+ case SAVE_LEVEL_AND_MAPS:
+ worldServer = this.levelAndMapsQueue.poll();
+ if(worldServer != null) {
+ worldServer.saveOnlyLevel(true, null);
+ }
+
+ if(this.levelAndMapsQueue.isEmpty()) {
+ this.moveToNextStep();
+ }
+
+ break;
+ case SAVE_CHUNKS:
+ worldServer = this.saveChunksQueue.peek();
+ if(worldServer != null) {
+ if(worldServer.saveOnlyChunks(false, null)) {
+ this.chunkQueuedCount += worldServer.getAutoSaveWorldData().getAutoSaveChunkCount();
+ this.saveChunksQueue.poll();
+ }
+ }
+
+ if(this.saveChunksQueue.isEmpty()) {
+ this.moveToNextStep();
+ }
+
+ break;
+ case UNLOAD_CHUNKS:
+ worldServer = this.unloadChunksQueue.poll();
+ if(worldServer != null) {
+ worldServer.unloadOnlyUnusedChunks(true, null);
+ }
+
+ if(this.unloadChunksQueue.isEmpty()) {
+ this.moveToNextStep();
+ }
+
+ break;
+ case WRITE_FILES_START:
+ FileIOThread.a.setNoDelay(true);
+ this.fileioStart = System.nanoTime();
+ this.moveToNextStep();
+ break;
+ case WRITE_FILES_WAIT:
+ if(FileIOThread.a.isDone()) {
+ this.fileioEnd = System.nanoTime();
+ FileIOThread.a.setNoDelay(false);
+ this.moveToNextStep();
+ }
+ break;
+ case FIRE_WORLDSAVEEVENT:
+ if(SpigotConfig.autoSaveClearRegionFileCache) {
+ this.regionFileCount = RegionFileCache.a.size();
+ this.regionFileCacheStart = System.nanoTime();
+ RegionFileCache.a();
+ this.regionFileCacheEnd = System.nanoTime();
+ }
+
+ if(SpigotConfig.autoSaveFireWorldSaveEvent) {
+ for(WorldServer worldserver: this.eventQueue) {
+ WorldSaveEvent event = new WorldSaveEvent(worldserver.getWorld());
+ MinecraftServer.getServer().server.getPluginManager().callEvent(event);
+ }
+ }
+ this.moveToNextStep();
+ break;
+ case FINISHED:
+ MinecraftServer.getLogger().info("[Autosave] Done. Queued " + this.chunkQueuedCount + " chunks for saving. Took " + formatLongTime(this.fileioEnd - this.fileioStart) + " seconds to write them.");
+ if(SpigotConfig.autoSaveClearRegionFileCache) {
+ MinecraftServer.getLogger().info("[Autosave] Cleared " + this.regionFileCount + " cached region files in " + formatLongTime(this.regionFileCacheEnd - this.regionFileCacheStart) + " seconds.");
+ }
+ this.reset();
+ return true;
+ }
+ return false;
+ }
+
+ private static String formatLongTime(long duration) {
+ return String.format("%.2f", ((double) (duration / 1000000L)) / 1000.0D);
+ }
+
+ public boolean isActive() {
+ return this.step.isActiveState();
+ }
+
+ public void start() {
+ this.step = AutoSaveStep.START;
+ }
+
+ public enum AutoSaveStep {
+ IDLE(false),
+ START(),
+ SAVE_PLAYERS(),
+ SAVE_LEVEL_AND_MAPS(),
+ SAVE_CHUNKS(),
+ UNLOAD_CHUNKS(),
+ WRITE_FILES_START(),
+ WRITE_FILES_WAIT(),
+ FIRE_WORLDSAVEEVENT(),
+ FINISHED();
+
+ private final boolean activeState;
+
+ private AutoSaveStep() {
+ this(true);
+ }
+
+ private AutoSaveStep(boolean activeState) {
+ this.activeState = activeState;
+ }
+
+ public static AutoSaveStep nextStep(AutoSaveStep current) {
+ AutoSaveStep[] values = AutoSaveStep.values();
+ if(current.ordinal() + 1 < values.length) {
+ return values[current.ordinal() + 1];
+ }
+ return current;
+ }
+
+ public boolean isActiveState() {
+ return this.activeState;
+ }
+ }
+}
diff --git a/src/main/java/net/frozenorb/autosave/AutoSaveWorldData.java b/src/main/java/net/frozenorb/autosave/AutoSaveWorldData.java
new file mode 100644
index 000000000..0139d6891
--- /dev/null
+++ b/src/main/java/net/frozenorb/autosave/AutoSaveWorldData.java
@@ -0,0 +1,33 @@
+package net.frozenorb.autosave;
+
+import net.minecraft.server.World;
+import net.minecraft.server.WorldData;
+
+public class AutoSaveWorldData {
+
+ private long lastAutoSaveTimeStamp;
+ private int autoSaveChunkCount;
+ private final World world;
+
+ public AutoSaveWorldData(World world) {
+ this.world = world;
+ this.setLastAutosaveTimeStamp();
+ }
+
+ public void setLastAutosaveTimeStamp() {
+ this.lastAutoSaveTimeStamp = this.world.worldData.getTime();
+ this.autoSaveChunkCount = 0;
+ }
+
+ public long getLastAutosaveTimeStamp() {
+ return this.lastAutoSaveTimeStamp;
+ }
+
+ public void addAutoSaveChunkCount(int count) {
+ this.autoSaveChunkCount += count;
+ }
+
+ public int getAutoSaveChunkCount() {
+ return this.autoSaveChunkCount;
+ }
+}
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
index 9e3e66806..d3453f644 100644
--- a/src/main/java/net/minecraft/server/Chunk.java
+++ b/src/main/java/net/minecraft/server/Chunk.java
@@ -931,10 +931,15 @@ public class Chunk {
if (this.o && this.world.getTime() != this.lastSaved || this.n) {
return true;
}
- } else if (this.o && this.world.getTime() >= this.lastSaved + MinecraftServer.getServer().autosavePeriod * 4) { // PaperSpigot - Only save if we've passed 2 auto save intervals without modification
+ // Poweruser start
+ } else if (this.n && this.world.getAutoSaveWorldData().getLastAutosaveTimeStamp() >= this.lastSaved) {
+ return true;
+ } else if (this.o && this.world.getTime() >= this.lastSaved + MinecraftServer.getServer().autosavePeriod) {
return true;
+ } else {
+ return false;
}
-
+ // Poweruser end
return this.n;
}
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index 1e297ab7e..b4ddc3bbf 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -331,11 +331,19 @@ public class ChunkProviderServer implements IChunkProvider {
this.saveChunk(chunk);
chunk.n = false;
++i;
- if (i == 24 && !flag) {
+ // Poweruser start
+ if (i >= org.spigotmc.SpigotConfig.autoSaveChunksPerTick && !flag) {
+ this.world.getAutoSaveWorldData().addAutoSaveChunkCount(i);
+ // Poweruser end
return false;
}
}
}
+ // Poweruser start
+ if(!flag) {
+ this.world.getAutoSaveWorldData().addAutoSaveChunkCount(i);
+ }
+ // Poweruser end
return true;
}
diff --git a/src/main/java/net/minecraft/server/FileIOThread.java b/src/main/java/net/minecraft/server/FileIOThread.java
index e9c333bfb..28488a2f8 100644
--- a/src/main/java/net/minecraft/server/FileIOThread.java
+++ b/src/main/java/net/minecraft/server/FileIOThread.java
@@ -67,4 +67,14 @@ public class FileIOThread implements Runnable {
this.e = false;
}
+
+ // Poweruser start
+ public boolean isDone() {
+ return this.c == this.d;
+ }
+
+ public void setNoDelay(boolean active) {
+ this.e = active;
+ }
+ // Poweruser end
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 36d95e7a9..81d36ab27 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -45,6 +45,7 @@ import org.bukkit.event.world.WorldSaveEvent;
// Poweruser start
import net.frozenorb.ThreadingManager;
+import net.frozenorb.autosave.AutoSave;
// Poweruser end
public abstract class MinecraftServer implements ICommandListener, Runnable, IMojangStatistics {
@@ -126,6 +127,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
// Poweruser start
private ThreadingManager threadingManager;
+ private AutoSave autoSaveManager;
// Poweruser end
public float lastTickTime = 0F; // MineHQ
@@ -136,6 +138,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
j = this;
this.d = proxy;
this.threadingManager = new ThreadingManager(); // Poweruser
+ this.autoSaveManager = new AutoSave(); // Poweruser
// this.universe = file1; // CraftBukkit
// this.p = new ServerConnection(this); // Spigot
this.o = new CommandDispatcher();
@@ -620,7 +623,22 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
this.q.b().a(agameprofile);
}
- if ((this.autosavePeriod > 0) && ((this.ticks % this.autosavePeriod) == 0)) { // CraftBukkit
+ // Poweruser start
+ if(this.autoSaveManager.isActive()) {
+ SpigotTimings.worldSaveTimer.startTiming(); // Spigot
+ server.playerCommandState = true;
+ this.autoSaveManager.execute();
+ server.playerCommandState = false;
+ SpigotTimings.worldSaveTimer.stopTiming(); // Spigot
+ } else if ((this.autosavePeriod > 0) && ((this.ticks % this.autosavePeriod) == 0)) { // CraftBukkit
+ this.autoSaveManager.reset();
+ for(WorldServer worldserver: worlds) {
+ this.autoSaveManager.queueWorld(worldserver);
+ }
+ this.autoSaveManager.start();
+ }
+ // Poweruser end
+ /*
SpigotTimings.worldSaveTimer.startTiming(); // Spigot
this.methodProfiler.a("save");
this.u.savePlayers();
@@ -638,6 +656,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
this.methodProfiler.b();
SpigotTimings.worldSaveTimer.stopTiming(); // Spigot
}
+ */
this.methodProfiler.a("tallying");
this.g[this.ticks % 100] = System.nanoTime() - i;
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index 609fe32ae..54e304a7d 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -42,6 +42,7 @@ import net.frozenorb.LightingUpdater;
import net.frozenorb.WeakChunkCache;
import net.frozenorb.ThreadingManager;
import net.frozenorb.ThreadingManager.TaskQueueWorker;
+import net.frozenorb.autosave.AutoSaveWorldData;
// Poweruser end
public abstract class World implements IBlockAccess {
@@ -212,6 +213,16 @@ public abstract class World implements IBlockAccess {
return this.worldProvider.e;
}
+
+ // Poweruser start
+ public ChunkProviderServer chunkProviderServer; // moved here from WorldServer
+ private final AutoSaveWorldData autoSaveWorldData;
+
+ public AutoSaveWorldData getAutoSaveWorldData() {
+ return this.autoSaveWorldData;
+ }
+ // Poweruser end
+
// CraftBukkit start
private final CraftWorld world;
public boolean pvpMode;
@@ -313,6 +324,8 @@ public abstract class World implements IBlockAccess {
if (!spigotConfig.mobsEnabled) {
this.world.setSpawnFlags(false, false);
}
+
+ this.autoSaveWorldData = new AutoSaveWorldData(this); // Poweruser
}
protected abstract IChunkProvider j();
diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
index a5d634fbb..8627ef832 100644
--- a/src/main/java/net/minecraft/server/WorldServer.java
+++ b/src/main/java/net/minecraft/server/WorldServer.java
@@ -337,7 +337,7 @@ public class WorldServer extends World {
this.timings.doTickTiles_tickingChunks_getChunk.startTiming(); // Poweruser
// Poweruser start
Chunk chunk = this.getChunkIfLoaded(chunkX, chunkZ);
- if(chunk == null || chunk.wasUnloaded() || !chunk.areNeighborsLoaded(1) || this.chunkProviderServer.unloadQueue.contains( chunkX, chunkZ )) {
+ if(chunk == null || !chunk.areNeighborsLoaded(1) || this.chunkProviderServer.unloadQueue.contains( chunkX, chunkZ )) {
iter.remove();
continue;
}
@@ -811,18 +811,30 @@ public class WorldServer extends World {
return this.worldProvider.h();
}
- public void save(boolean flag, IProgressUpdate iprogressupdate) throws ExceptionWorldConflict { // CraftBukkit - added throws
+ // Poweruser start
+ public void saveOnlyLevel(boolean flag, IProgressUpdate iprogressupdate) throws ExceptionWorldConflict {
if (this.chunkProvider.canSave()) {
if (iprogressupdate != null) {
iprogressupdate.a("Saving level");
}
this.a();
+ }
+ }
+
+ public boolean saveOnlyChunks(boolean flag, IProgressUpdate iprogressupdate) {
+ if (this.chunkProvider.canSave()) {
if (iprogressupdate != null) {
iprogressupdate.c("Saving chunks");
}
- this.chunkProvider.saveChunks(flag, iprogressupdate);
+ return this.chunkProvider.saveChunks(flag, iprogressupdate);
+ }
+ return true;
+ }
+
+ public void unloadOnlyUnusedChunks(boolean flag, IProgressUpdate iprogressupdate) {
+ if (this.chunkProvider.canSave()) {
// CraftBukkit - ArrayList -> Collection
Collection arraylist = this.chunkProviderServer.a();
Iterator iterator = arraylist.iterator();
@@ -836,6 +848,17 @@ public class WorldServer extends World {
}
}
}
+ // Poweruser end
+
+ public void save(boolean flag, IProgressUpdate iprogressupdate) throws ExceptionWorldConflict { // CraftBukkit - added throws
+ if (this.chunkProvider.canSave()) {
+ // Poweruser start
+ this.saveOnlyLevel(flag, iprogressupdate);
+ this.saveOnlyChunks(flag, iprogressupdate);
+ this.unloadOnlyUnusedChunks(flag, iprogressupdate);
+ // Poweruser end
+ }
+ }
public void flushSave() {
if (this.chunkProvider.canSave()) {
diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java
index eff9a948e..c2d272c5f 100644
--- a/src/main/java/org/spigotmc/SpigotConfig.java
+++ b/src/main/java/org/spigotmc/SpigotConfig.java
@@ -430,5 +430,20 @@ public class SpigotConfig
private static void playersPerChunkIOThread() {
playersPerChunkIOThread = Math.max(1, getInt( "settings.chunkio.players-per-thread", 150) );
}
+
+ public static int autoSaveChunksPerTick;
+ private static void autoSaveChunksPerTick() {
+ autoSaveChunksPerTick = getInt( "settings.autosave.chunks-per-tick" , 200 );
+ }
+
+ public static boolean autoSaveFireWorldSaveEvent;
+ private static void autoSaveFireWorldSaveEvent() {
+ autoSaveFireWorldSaveEvent = getBoolean ( "settings.autosave.fire-WorldSaveEvent", false);
+ }
+
+ public static boolean autoSaveClearRegionFileCache;
+ private static void autoSaveClearRegionFileCache() {
+ autoSaveClearRegionFileCache = getBoolean ( "settings.autosave.clear-RegionFileCache", false);
+ }
// Poweruser end
}
--
2.13.3