Add config options to paper.yml for Async Chunks and Shared Host Overrides
Shared Hosts have been concerned over the increased CPU usage that Async Chunks has introduced. We understand that shared hosts can't allow 1 client to abuse the systme resources depriving other servers of cpu time, so we've added some configs to allow hosts to force some settings. Hosts, you may set the following SYSTEM ENVIRONMENT VARIABLES (NOT JVM FLAGS) to change some behavior: PAPER_ASYNC_CHUNKS_SHARED_HOST_GEN=1 - Enable Single Threaded World Generation (capping CPU Usage PAPER_ASYNC_CHUNKS_SHARED_HOST_GEN=2 - Disable Async Chunk Generation - Please do not do this!!! - Minecraft is moving towards asynchronousm per-world chunk generation itself. You need to prepare - for this, so please do not disable async chunk generation. PAPER_ASYNC_CHUNKS_SHARED_HOST_LOAD=# - Where # is Maximum number of threads to use on Chunk Loading - This is a max, so your end user can reduce it more too. I suggest no lower than 2. Minimum is 1. - These are usually much faster request, so outside of loading entire worlds, They usually do not - get as much load. Paperby default uses CPU Core count * 1.5, so this may default to a higher value - on shared host and you should override it with this value to say 2-4. These settings will override your clients paper.yml options. Set as a system wide environment variable to ensure all Paper clients respect your resource usage desires. Resolves #1520
This commit is contained in:
@@ -43,7 +43,7 @@ reading or writing to the chunk will be safe, so plugins still
|
||||
should not be touching chunks asynchronously!
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
index da710cc6fe..6b65364e22 100644
|
||||
index 912c990404..2f6d7f2976 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
@@ -0,0 +0,0 @@ public class PaperConfig {
|
||||
@@ -51,14 +51,56 @@ index da710cc6fe..6b65364e22 100644
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Temporary opt out, will be removed later
|
||||
+ public static boolean asyncChunks = false;
|
||||
+ private static void disableAsyncChunks() {
|
||||
+ asyncChunks = config.getBoolean("settings.async-chunks", true);
|
||||
+ public static boolean asyncChunkGeneration = true;
|
||||
+ public static boolean asyncChunkGenThreadPerWorld = true;
|
||||
+ public static int asyncChunkLoadThreads = -1;
|
||||
+ private static void asyncChunks() {
|
||||
+ if (version < 15) {
|
||||
+ boolean enabled = config.getBoolean("settings.async-chunks", true);
|
||||
+ ConfigurationSection section = config.createSection("settings.async-chunks");
|
||||
+ section.set("enable", enabled);
|
||||
+ section.set("load-threads", -1);
|
||||
+ section.set("generation", true);
|
||||
+ section.set("thread-per-world-generation", true);
|
||||
+ }
|
||||
+
|
||||
+ asyncChunks = getBoolean("settings.async-chunks.enable", true);
|
||||
+ asyncChunkGeneration = getBoolean("settings.async-chunks.generation", true);
|
||||
+ asyncChunkGenThreadPerWorld = getBoolean("settings.async-chunks.thread-per-world-generation", true);
|
||||
+ asyncChunkLoadThreads = getInt("settings.async-chunks.load-threads", -1);
|
||||
+ if (asyncChunkLoadThreads <= 0) {
|
||||
+ asyncChunkLoadThreads = (int) Math.min(Integer.getInteger("paper.maxchunkthreads", 8), Runtime.getRuntime().availableProcessors() * 1.5);
|
||||
+ }
|
||||
+
|
||||
+ // Let Shared Host set some limits
|
||||
+ String sharedHostEnvGen = System.getenv("PAPER_ASYNC_CHUNKS_SHARED_HOST_GEN");
|
||||
+ String sharedHostEnvLoad = System.getenv("PAPER_ASYNC_CHUNKS_SHARED_HOST_LOAD");
|
||||
+ if ("1".equals(sharedHostEnvGen)) {
|
||||
+ log("Async Chunks - Generation: Your host has requested to use a single thread world generation");
|
||||
+ asyncChunkGenThreadPerWorld = false;
|
||||
+ } else if ("2".equals(sharedHostEnvGen)) {
|
||||
+ log("Async Chunks - Generation: Your host has disabled async world generation - You will experience lag from world generation");
|
||||
+ asyncChunkGeneration = false;
|
||||
+ }
|
||||
+
|
||||
+ if (sharedHostEnvLoad != null) {
|
||||
+ try {
|
||||
+ asyncChunkLoadThreads = Math.max(1, Math.min(asyncChunkLoadThreads, Integer.parseInt(sharedHostEnvLoad)));
|
||||
+ } catch (NumberFormatException ignored) {}
|
||||
+ }
|
||||
+
|
||||
+ if (!asyncChunks) {
|
||||
+ log("Async Chunks: Disabled - Chunks will be managed synchronosuly, and will cause tremendous lag.");
|
||||
+ } else {
|
||||
+ log("Async Chunks: Enabled - Chunks will be loaded and generated much faster, without lag.");
|
||||
+ log("Async Chunks: Enabled - Chunks will be loaded much faster, without lag.");
|
||||
+ if (!asyncChunkGeneration) {
|
||||
+ log("Async Chunks - Generation: Disabled - Chunks will be generated synchronosuly, and will cause tremendous lag.");
|
||||
+ } else if (asyncChunkGenThreadPerWorld) {
|
||||
+ log("Async Chunks - Generation: Enabled - Chunks will be generated much faster, without lag.");
|
||||
+ } else {
|
||||
+ log("Async Chunks - Generation: Enabled (Single Thread) - Chunks will be generated much faster, without lag.");
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
@@ -794,7 +836,7 @@ index 98d182fdb8..487d98eb1b 100644
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java b/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java
|
||||
new file mode 100644
|
||||
index 0000000000..a3fc989832
|
||||
index 0000000000..1c592c7956
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java
|
||||
@@ -0,0 +0,0 @@
|
||||
@@ -846,8 +888,8 @@ index 0000000000..a3fc989832
|
||||
+public class PaperAsyncChunkProvider extends ChunkProviderServer {
|
||||
+
|
||||
+ private static final PriorityQueuedExecutor.RejectionHandler IGNORE_HANDLER = (run, executor) -> {};
|
||||
+ private static final int CHUNK_THREADS = (int) Math.min(Integer.getInteger("paper.maxchunkthreads", 8), Runtime.getRuntime().availableProcessors() * 1.5);
|
||||
+ private static final PriorityQueuedExecutor EXECUTOR = new PriorityQueuedExecutor("PaperChunkLoader", PaperConfig.asyncChunks ? CHUNK_THREADS : 0, IGNORE_HANDLER);
|
||||
+ private static final PriorityQueuedExecutor EXECUTOR = new PriorityQueuedExecutor("PaperChunkLoader", PaperConfig.asyncChunks ? PaperConfig.asyncChunkLoadThreads : 0, IGNORE_HANDLER);
|
||||
+ private static final PriorityQueuedExecutor SINGLE_GEN_EXECUTOR = new PriorityQueuedExecutor("PaperChunkGenerator", PaperConfig.asyncChunks && PaperConfig.asyncChunkGeneration && !PaperConfig.asyncChunkGenThreadPerWorld ? 1 : 0, IGNORE_HANDLER);
|
||||
+
|
||||
+ private final PriorityQueuedExecutor generationExecutor;
|
||||
+ //private static final PriorityQueuedExecutor generationExecutor = new PriorityQueuedExecutor("PaperChunkGen", 1);
|
||||
@@ -868,8 +910,8 @@ index 0000000000..a3fc989832
|
||||
+ this.asyncHandler = server;
|
||||
+ this.chunkLoader = chunkLoader;
|
||||
+ String worldName = this.world.getWorld().getName();
|
||||
+ this.shouldGenSync = generator instanceof CustomChunkGenerator && !(((CustomChunkGenerator) generator).asyncSupported);
|
||||
+ this.generationExecutor = new PriorityQueuedExecutor("PaperChunkGen-" + worldName, shouldGenSync ? 0 : 1, IGNORE_HANDLER);
|
||||
+ this.shouldGenSync = generator instanceof CustomChunkGenerator && !(((CustomChunkGenerator) generator).asyncSupported) || !PaperConfig.asyncChunkGeneration;
|
||||
+ this.generationExecutor = PaperConfig.asyncChunkGenThreadPerWorld ? new PriorityQueuedExecutor("PaperChunkGen-" + worldName, shouldGenSync ? 0 : 1, IGNORE_HANDLER) : SINGLE_GEN_EXECUTOR;
|
||||
+ }
|
||||
+
|
||||
+ static void processChunkLoads(MinecraftServer server) {
|
||||
@@ -1176,13 +1218,12 @@ index 0000000000..a3fc989832
|
||||
+ this.chunk = chunk;
|
||||
+ this.hasFinished = true;
|
||||
+ }
|
||||
+ if (Bukkit.isPrimaryThread()) {
|
||||
+ postChunk();
|
||||
+ } else {
|
||||
+ synchronized (mainThreadQueue) {
|
||||
+ mainThreadQueue.add(this::postChunk);
|
||||
+ mainThreadQueue.notify();
|
||||
+ }
|
||||
+
|
||||
+ // Don't post here, even if on main, it must enter the queue so we can exit any open batch
|
||||
+ // schedulers, as post stage may trigger a new generation and cause errors
|
||||
+ synchronized (mainThreadQueue) {
|
||||
+ mainThreadQueue.add(this::postChunk);
|
||||
+ mainThreadQueue.notify();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
@@ -1410,7 +1451,7 @@ index 3c35c0f481..187ca2813a 100644
|
||||
this.m = world.getChunkProvider().getChunkGenerator().getSettings();
|
||||
this.i = world.getSeaLevel();
|
||||
diff --git a/src/main/java/net/minecraft/server/SchedulerBatch.java b/src/main/java/net/minecraft/server/SchedulerBatch.java
|
||||
index d868149d1a..0d94b262ac 100644
|
||||
index d868149d1a..0d45d933ee 100644
|
||||
--- a/src/main/java/net/minecraft/server/SchedulerBatch.java
|
||||
+++ b/src/main/java/net/minecraft/server/SchedulerBatch.java
|
||||
@@ -0,0 +0,0 @@ public class SchedulerBatch<K, T extends SchedulerTask<K, T>, R> {
|
||||
@@ -1427,10 +1468,12 @@ index d868149d1a..0d94b262ac 100644
|
||||
|
||||
+ public void startBatch() { b(); } // Paper - OBFHELPER
|
||||
public void b() {
|
||||
- if (this.c) {
|
||||
+ lock.lock(); // Paper
|
||||
if (this.c) {
|
||||
+ if (false && this.c) { // Paper
|
||||
throw new RuntimeException("Batch already started.");
|
||||
} else {
|
||||
this.d = 1000;
|
||||
@@ -0,0 +0,0 @@ public class SchedulerBatch<K, T extends SchedulerTask<K, T>, R> {
|
||||
}
|
||||
}
|
||||
@@ -1445,14 +1488,16 @@ index d868149d1a..0d94b262ac 100644
|
||||
|
||||
+ public CompletableFuture<R> executeBatch() { return c(); } // Paper - OBFHELPER
|
||||
public CompletableFuture<R> c() {
|
||||
- if (!this.c) {
|
||||
+ // Paper start
|
||||
+ if (!lock.isHeldByCurrentThread()) {
|
||||
+ throw new IllegalStateException("Current thread does not hold the write lock");
|
||||
+ }
|
||||
+ try {// Paper end
|
||||
if (!this.c) {
|
||||
+ if (false && !this.c) { // Paper
|
||||
throw new RuntimeException("Batch not properly started. Please use startBatch to create a new batch.");
|
||||
} else {
|
||||
if (this.d != 1000) {
|
||||
@@ -0,0 +0,0 @@ public class SchedulerBatch<K, T extends SchedulerTask<K, T>, R> {
|
||||
this.c = false;
|
||||
return this.b.c();
|
||||
|
||||
Reference in New Issue
Block a user