Yatopia/patches/Tuinity/patches/server/0015-Execute-chunk-tasks-mi...

344 lines
17 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Mon, 6 Apr 2020 04:20:44 -0700
Subject: [PATCH] Execute chunk tasks mid-tick
This will help the server load chunks if tick times are high.
diff --git a/src/main/java/com/destroystokyo/paper/server/ticklist/PaperTickList.java b/src/main/java/com/destroystokyo/paper/server/ticklist/PaperTickList.java
index 4666d6582535d6e49c5bd40d4fcdcdfe07590aa9..b870cca05f0ba354e6976a70511235636093d13c 100644
--- a/src/main/java/com/destroystokyo/paper/server/ticklist/PaperTickList.java
+++ b/src/main/java/com/destroystokyo/paper/server/ticklist/PaperTickList.java
@@ -310,6 +310,7 @@ public final class PaperTickList<T> extends TickListServer<T> { // extend to avo
if (toTick.tickState == STATE_TICKING) {
toTick.tickState = STATE_TICKED;
} // else it's STATE_CANCELLED_TICK
+ MinecraftServer.getServer().executeMidTickTasks(); // Tuinity - exec chunk tasks during world tick
} else {
// re-schedule eventually
toTick.tickState = STATE_SCHEDULED;
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 419d17d0eb9426e4f3f9f97a504e567401d175f3..dae8824a6034cdaaecbedeba5e05c62f4565583f 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1115,7 +1115,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
// Paper end
tickSection = curTime;
}
- midTickChunksTasksRan = 0; // Paper
+ // Tuinity - replace logic
// Spigot end
//MinecraftServer.currentTick = (int) (System.currentTimeMillis() / 50); // CraftBukkit // Paper - don't overwrite current tick time
@@ -1208,6 +1208,76 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
}
// Paper end
+ // Tuinity start - execute chunk tasks mid tick
+ static final long CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME = 25L * 1000L; // 25us
+ static final long MAX_CHUNK_EXEC_TIME = 1000L; // 1us
+
+ static final long TASK_EXECUTION_FAILURE_BACKOFF = 5L * 1000L; // 5us
+
+ private static long lastMidTickExecute;
+ private static long lastMidTickExecuteFailure;
+
+ private boolean tickMidTickTasks() {
+ // give all worlds a fair chance at by targetting them all.
+ // if we execute too many tasks, that's fine - we have logic to correctly handle overuse of allocated time.
+ boolean executed = false;
+ for (WorldServer world : this.getWorlds()) {
+ long currTime = System.nanoTime();
+ if (currTime - world.lastMidTickExecuteFailure <= TASK_EXECUTION_FAILURE_BACKOFF) {
+ continue;
+ }
+ if (!world.getChunkProvider().runTasks()) {
+ // we need to back off if this fails
+ world.lastMidTickExecuteFailure = currTime;
+ } else {
+ executed = true;
+ }
+ }
+
+ return executed;
+ }
+
+ public final void executeMidTickTasks() {
+ org.spigotmc.AsyncCatcher.catchOp("mid tick chunk task execution");
+ long startTime = System.nanoTime();
+ if ((startTime - lastMidTickExecute) <= CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME || (startTime - lastMidTickExecuteFailure) <= TASK_EXECUTION_FAILURE_BACKOFF) {
+ // it's shown to be bad to constantly hit the queue (chunk loads slow to a crawl), even if no tasks are executed.
+ // so, backoff to prevent this
+ return;
+ }
+
+ co.aikar.timings.MinecraftTimings.midTickChunkTasks.startTiming();
+ try {
+ for (;;) {
+ boolean moreTasks = this.tickMidTickTasks();
+ long currTime = System.nanoTime();
+ long diff = currTime - startTime;
+
+ if (!moreTasks || diff >= MAX_CHUNK_EXEC_TIME) {
+ if (!moreTasks) {
+ lastMidTickExecuteFailure = currTime;
+ }
+
+ // note: negative values reduce the time
+ long overuse = diff - MAX_CHUNK_EXEC_TIME;
+ if (overuse >= (10L * 1000L * 1000L)) { // 10ms
+ // make sure something like a GC or dumb plugin doesn't screw us over...
+ overuse = 10L * 1000L * 1000L; // 10ms
+ }
+
+ double overuseCount = (double)overuse/(double)MAX_CHUNK_EXEC_TIME;
+ long extraSleep = (long)Math.round(overuseCount*CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME);
+
+ lastMidTickExecute = currTime + extraSleep;
+ return;
+ }
+ }
+ } finally {
+ co.aikar.timings.MinecraftTimings.midTickChunkTasks.stopTiming();
+ }
+ }
+ // Tuinity end - execute chunk tasks mid tick
+
private void executeModerately() {
this.executeAll();
java.util.concurrent.locks.LockSupport.parkNanos("executing tasks", 1000L);
@@ -1221,22 +1291,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
});
}
- // Paper start
- public int midTickChunksTasksRan = 0;
- private long midTickLastRan = 0;
- public void midTickLoadChunks() {
- if (!isMainThread() || System.nanoTime() - midTickLastRan < 1000000) {
- // only check once per 0.25ms incase this code is called in a hot method
- return;
- }
- try (co.aikar.timings.Timing ignored = co.aikar.timings.MinecraftTimings.midTickChunkTasks.startTiming()) {
- for (WorldServer value : this.getWorlds()) {
- value.getChunkProvider().serverThreadQueue.midTickLoadChunks();
- }
- midTickLastRan = System.nanoTime();
- }
- }
- // Paper end
+ // Tuinity - replace logic
@Override
public TickTask postToMainThread(Runnable runnable) {
@@ -1263,6 +1318,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
private boolean bb() {
if (super.executeNext()) {
+ this.executeMidTickTasks(); // Tuinity - execute chunk tasks mid tick
return true;
} else {
if (this.canSleepForTick()) {
@@ -1330,7 +1386,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
// Paper start - move oversleep into full server tick
isOversleep = true;MinecraftTimings.serverOversleep.startTiming();
this.awaitTasks(() -> {
- midTickLoadChunks(); // will only do loads since we are still considered !canSleepForTick
+ // Tuinity - replace logic
return !this.canOversleep();
});
isOversleep = false;MinecraftTimings.serverOversleep.stopTiming();
@@ -1421,16 +1477,16 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
}
protected void b(BooleanSupplier booleansupplier) {
- midTickLoadChunks(); // Paper
+ // Tuinity - replace logic
MinecraftTimings.bukkitSchedulerTimer.startTiming(); // Spigot // Paper
this.server.getScheduler().mainThreadHeartbeat(this.ticks); // CraftBukkit
MinecraftTimings.bukkitSchedulerTimer.stopTiming(); // Spigot // Paper
- midTickLoadChunks(); // Paper
+ // Tuinity - replace logic
this.methodProfiler.enter("commandFunctions");
MinecraftTimings.commandFunctionsTimer.startTiming(); // Spigot // Paper
this.getFunctionData().tick();
MinecraftTimings.commandFunctionsTimer.stopTiming(); // Spigot // Paper
- midTickLoadChunks(); // Paper
+ // Tuinity - replace logic
this.methodProfiler.exitEnter("levels");
Iterator iterator = this.getWorlds().iterator();
@@ -1441,7 +1497,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
processQueue.remove().run();
}
MinecraftTimings.processQueueTimer.stopTiming(); // Spigot
- midTickLoadChunks(); // Paper
+ // Tuinity - replace logic
MinecraftTimings.timeUpdateTimer.startTiming(); // Spigot // Paper
// Send time updates to everyone, it will get the right time from the world the player is in.
// Paper start - optimize time updates
@@ -1484,7 +1540,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
this.methodProfiler.enter("tick");
try {
- midTickLoadChunks(); // Paper
+ // Tuinity - replace logic
worldserver.timings.doTick.startTiming(); // Spigot
worldserver.doTick(booleansupplier);
// Tuinity start
@@ -1493,7 +1549,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
}
// Tuinity end
worldserver.timings.doTick.stopTiming(); // Spigot
- midTickLoadChunks(); // Paper
+ // Tuinity - replace logic
} catch (Throwable throwable) {
// Spigot Start
CrashReport crashreport;
diff --git a/src/main/java/net/minecraft/server/level/ChunkProviderServer.java b/src/main/java/net/minecraft/server/level/ChunkProviderServer.java
index c5038aa66703484a9243579ba4a6c92d138fc388..e38e507ccc26058b4f4d2e8fe061188525e7ac92 100644
--- a/src/main/java/net/minecraft/server/level/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/level/ChunkProviderServer.java
@@ -918,7 +918,7 @@ public class ChunkProviderServer extends IChunkProvider {
this.world.getMethodProfiler().enter("purge");
this.world.timings.doChunkMap.startTiming(); // Spigot
this.chunkMapDistance.purgeTickets();
- this.world.getMinecraftServer().midTickLoadChunks(); // Paper
+ // Tuinity - replace logic
this.tickDistanceManager();
this.world.timings.doChunkMap.stopTiming(); // Spigot
this.world.getMethodProfiler().exitEnter("chunks");
@@ -928,7 +928,7 @@ public class ChunkProviderServer extends IChunkProvider {
this.world.timings.doChunkUnload.startTiming(); // Spigot
this.world.getMethodProfiler().exitEnter("unload");
this.playerChunkMap.unloadChunks(booleansupplier);
- this.world.getMinecraftServer().midTickLoadChunks(); // Paper
+ // Tuinity - replace logic
this.world.timings.doChunkUnload.stopTiming(); // Spigot
this.world.getMethodProfiler().exit();
this.clearCache();
@@ -1000,6 +1000,8 @@ public class ChunkProviderServer extends IChunkProvider {
// Paper end
this.world.timings.countNaturalMobs.stopTiming(); // Paper - timings
+ int ticked = 0; // Tuinity - exec chunk tasks during world tick
+
this.p = spawnercreature_d;
this.world.getMethodProfiler().exit();
//List<PlayerChunk> list = Lists.newArrayList(this.playerChunkMap.f()); // Paper
@@ -1029,7 +1031,7 @@ public class ChunkProviderServer extends IChunkProvider {
this.world.timings.chunkTicks.startTiming(); // Spigot // Paper
this.world.a(chunk, k);
this.world.timings.chunkTicks.stopTiming(); // Spigot // Paper
- if (chunksTicked[0]++ % 10 == 0) this.world.getMinecraftServer().midTickLoadChunks(); // Paper
+ if ((++ticked & 1) == 0) MinecraftServer.getServer().executeMidTickTasks(); // Tuinity - exec chunk tasks during world tick
}
}
}
@@ -1185,41 +1187,7 @@ public class ChunkProviderServer extends IChunkProvider {
ChunkProviderServer.this.world.getMethodProfiler().c("runTask");
super.executeTask(runnable);
}
-
- // Paper start
- private long lastMidTickChunkTask = 0;
- public boolean pollChunkLoadTasks() {
- if (com.destroystokyo.paper.io.chunk.ChunkTaskManager.pollChunkWaitQueue() || ChunkProviderServer.this.world.asyncChunkTaskManager.pollNextChunkTask()) {
- try {
- ChunkProviderServer.this.tickDistanceManager();
- } finally {
- // from below: process pending Chunk loadCallback() and unloadCallback() after each run task
- playerChunkMap.callbackExecutor.run();
- }
- return true;
- }
- return false;
- }
- public void midTickLoadChunks() {
- MinecraftServer server = ChunkProviderServer.this.world.getMinecraftServer();
- // always try to load chunks, restrain generation/other updates only. don't count these towards tick count
- //noinspection StatementWithEmptyBody
- while (pollChunkLoadTasks()) {}
-
- if (System.nanoTime() - lastMidTickChunkTask < 200000) {
- return;
- }
-
- for (;server.midTickChunksTasksRan < com.destroystokyo.paper.PaperConfig.midTickChunkTasks && server.canSleepForTick();) {
- if (this.executeNext()) {
- server.midTickChunksTasksRan++;
- lastMidTickChunkTask = System.nanoTime();
- } else {
- break;
- }
- }
- }
- // Paper end
+ // Tuinity - replace logic
@Override
protected boolean executeNext() {
diff --git a/src/main/java/net/minecraft/server/level/WorldServer.java b/src/main/java/net/minecraft/server/level/WorldServer.java
index 25073eb392bb00daf7e0b68be4767cea416c1816..1658c03b70b5984c87d069276efcc7b8c8878431 100644
--- a/src/main/java/net/minecraft/server/level/WorldServer.java
+++ b/src/main/java/net/minecraft/server/level/WorldServer.java
@@ -410,6 +410,10 @@ public class WorldServer extends World implements GeneratorAccessSeed {
}
// Tuinity end
+ // Tuinity start - execute chunk tasks mid tick
+ public long lastMidTickExecuteFailure;
+ // Tuinity end - execute chunk tasks mid tick
+
// Add env and gen to constructor, WorldData -> WorldDataServer
public WorldServer(MinecraftServer minecraftserver, Executor executor, Convertable.ConversionSession convertable_conversionsession, IWorldDataServer iworlddataserver, ResourceKey<World> resourcekey, DimensionManager dimensionmanager, WorldLoadListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List<MobSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) {
super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getMethodProfiler, false, flag, i, gen, env, executor); // Paper pass executor
@@ -674,7 +678,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
}
timings.scheduledBlocks.stopTiming(); // Paper
- this.getMinecraftServer().midTickLoadChunks(); // Paper
+ // Tuinity - replace logic
gameprofilerfiller.exitEnter("raid");
this.timings.raids.startTiming(); // Paper - timings
this.persistentRaid.a();
@@ -683,7 +687,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
timings.doSounds.startTiming(); // Spigot
this.ak();
timings.doSounds.stopTiming(); // Spigot
- this.getMinecraftServer().midTickLoadChunks(); // Paper
+ // Tuinity - replace logic
this.ticking = false;
gameprofilerfiller.exitEnter("entities");
boolean flag3 = true || !this.players.isEmpty() || !this.getForceLoadedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players
@@ -760,7 +764,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
}
this.afterEntityTickingTasks.clear();
// Paper end
- this.getMinecraftServer().midTickLoadChunks(); // Paper
+ // Tuinity - replace logic
Entity entity2;
@@ -770,7 +774,7 @@ public class WorldServer extends World implements GeneratorAccessSeed {
}
timings.tickEntities.stopTiming(); // Spigot
- this.getMinecraftServer().midTickLoadChunks(); // Paper
+ // Tuinity - replace logic
this.tickBlockEntities();
}
diff --git a/src/main/java/net/minecraft/world/level/World.java b/src/main/java/net/minecraft/world/level/World.java
index 74894282575d12f25ba4968abf873d3a346529b7..89d43183cf6c628c124aba486db69336f1e017df 100644
--- a/src/main/java/net/minecraft/world/level/World.java
+++ b/src/main/java/net/minecraft/world/level/World.java
@@ -960,6 +960,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
return;
// Paper end
}
+ MinecraftServer.getServer().executeMidTickTasks(); // Tuinity - execute chunk tasks mid tick
}
// Paper start - Prevent armor stands from doing entity lookups
@Override