mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-30 16:19:03 +01:00
182 lines
8.8 KiB
Diff
182 lines
8.8 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/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java
|
|
index b27021a42cbed3f0648a8d0903d00d03922ae221..eada966d7f108a6081be7a848f5c1dfcb1eed676 100644
|
|
--- a/src/main/java/co/aikar/timings/MinecraftTimings.java
|
|
+++ b/src/main/java/co/aikar/timings/MinecraftTimings.java
|
|
@@ -45,6 +45,8 @@ public final class MinecraftTimings {
|
|
public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update");
|
|
public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate");
|
|
|
|
+ public static final Timing midTickChunkTasks = Timings.ofSafe("Mid Tick Chunk Tasks");
|
|
+
|
|
private static final Map<Class<?>, String> taskNameCache = new MapMaker().weakKeys().makeMap();
|
|
|
|
private MinecraftTimings() {}
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index 8aaf73d24ddff65c45eeb18d573e20fb8a1439a5..fddb279d161b30d6944495f0de9f6b7e21761a80 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -1344,6 +1344,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
|
|
private boolean pollTaskInternal() {
|
|
if (super.pollTask()) {
|
|
+ this.executeMidTickTasks(); // Paper - execute chunk tasks mid tick
|
|
return true;
|
|
} else {
|
|
if (this.haveTime()) {
|
|
@@ -2665,4 +2666,74 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
}
|
|
// Paper end
|
|
+
|
|
+ // Paper 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 (ServerLevel world : this.getAllLevels()) {
|
|
+ long currTime = System.nanoTime();
|
|
+ if (currTime - world.lastMidTickExecuteFailure <= TASK_EXECUTION_FAILURE_BACKOFF) {
|
|
+ continue;
|
|
+ }
|
|
+ if (!world.getChunkSource().pollTask()) {
|
|
+ // 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();
|
|
+ }
|
|
+ }
|
|
+ // Paper end - execute chunk tasks mid tick
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
index d06ad4f2c76eebd3b8191f3de6c0199fa3bb5f22..e14215d538d19838041e3e21a84396c2f31ecd8e 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -1011,6 +1011,7 @@ public class ServerChunkCache extends ChunkSource {
|
|
iterator1 = shuffled.iterator();
|
|
}
|
|
|
|
+ int chunksTicked = 0; // Paper
|
|
try {
|
|
while (iterator1.hasNext()) {
|
|
LevelChunk chunk1 = iterator1.next();
|
|
@@ -1028,6 +1029,7 @@ public class ServerChunkCache extends ChunkSource {
|
|
|
|
if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) {
|
|
this.level.tickChunk(chunk1, k);
|
|
+ if ((chunksTicked++ & 1) == 0) net.minecraft.server.MinecraftServer.getServer().executeMidTickTasks(); // Paper
|
|
}
|
|
}
|
|
// Paper start - optimise chunk tick iteration
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
index 848601bf0a5af305a0eef48d5870afc0fcce3af0..8fc78a0405359e6031e66e988f3ddbf913ca59bd 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -202,7 +202,9 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
private final StructureFeatureManager structureFeatureManager;
|
|
private final StructureCheck structureCheck;
|
|
private final boolean tickTime;
|
|
-
|
|
+ // Paper start - execute chunk tasks mid tick
|
|
+ public long lastMidTickExecuteFailure;
|
|
+ // Paper end - execute chunk tasks mid tick
|
|
|
|
// CraftBukkit start
|
|
private int tickPosition;
|
|
@@ -965,6 +967,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
if (fluid1.is(fluid)) {
|
|
fluid1.tick(this, pos);
|
|
}
|
|
+ MinecraftServer.getServer().executeMidTickTasks(); // Paper - exec chunk tasks during world tick
|
|
|
|
}
|
|
|
|
@@ -974,6 +977,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
if (iblockdata.is(block)) {
|
|
iblockdata.tick(this, pos, this.random);
|
|
}
|
|
+ MinecraftServer.getServer().executeMidTickTasks(); // Paper - exec chunk tasks during world tick
|
|
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
|
index 944519c4433710610ac5015d3d3de380d9ec39c9..06035b728a66a63582c34c85096bada1588bfaa6 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -838,6 +838,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
// Spigot end
|
|
} else if (this.shouldTickBlocksAt(ChunkPos.asLong(tickingblockentity.getPos()))) {
|
|
tickingblockentity.tick();
|
|
+ // Paper start - execute chunk tasks during tick
|
|
+ if ((this.tileTickPosition & 7) == 0) {
|
|
+ MinecraftServer.getServer().executeMidTickTasks();
|
|
+ }
|
|
+ // Paper end - execute chunk tasks during tick
|
|
}
|
|
}
|
|
this.blockEntityTickers.removeAll(toRemove);
|
|
@@ -852,6 +857,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
public <T extends Entity> void guardEntityTick(Consumer<T> tickConsumer, T entity) {
|
|
try {
|
|
tickConsumer.accept(entity);
|
|
+ MinecraftServer.getServer().executeMidTickTasks(); // Paper - execute chunk tasks mid tick
|
|
} catch (Throwable throwable) {
|
|
if (throwable instanceof ThreadDeath) throw throwable; // Paper
|
|
// Paper start - Prevent tile entity and entity crashes
|