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:
Aikar 2018-10-02 23:42:29 -04:00
parent 10209bfbd8
commit a64b0952cb
No known key found for this signature in database
GPG key ID: 401ADFC9891FAAFE
2 changed files with 82 additions and 37 deletions

View file

@ -1,4 +1,4 @@
From 800d7c783d6ed1be96b675b15a31a04c0cf3bb6e Mon Sep 17 00:00:00 2001
From a8159e0501c03d8ee285f2bd6d19bdf3e52d5cf8 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach.brown@destroystokyo.com>
Date: Mon, 29 Feb 2016 21:02:09 -0600
Subject: [PATCH] Paper config files
@ -6,7 +6,7 @@ Subject: [PATCH] Paper config files
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
new file mode 100644
index 000000000..961966e16
index 0000000000..961966e169
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
@@ -0,0 +1,237 @@
@ -249,7 +249,7 @@ index 000000000..961966e16
+}
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
new file mode 100644
index 000000000..104abab84
index 0000000000..92e44befe0
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
@@ -0,0 +1,177 @@
@ -313,7 +313,7 @@ index 000000000..104abab84
+ commands.put("paper", new PaperCommand("paper"));
+
+ version = getInt("config-version", 14);
+ set("config-version", 14);
+ set("config-version", 15);
+ readConfig(PaperConfig.class, null);
+ }
+
@ -432,7 +432,7 @@ index 000000000..104abab84
+}
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
new file mode 100644
index 000000000..a73865739
index 0000000000..a738657394
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -0,0 +1,67 @@
@ -504,7 +504,7 @@ index 000000000..a73865739
+ }
+}
diff --git a/src/main/java/net/minecraft/server/DedicatedServer.java b/src/main/java/net/minecraft/server/DedicatedServer.java
index 8b3988fc0..d8535fdd9 100644
index 8b3988fc0a..d8535fdd9e 100644
--- a/src/main/java/net/minecraft/server/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/DedicatedServer.java
@@ -192,6 +192,10 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
@ -519,7 +519,7 @@ index 8b3988fc0..d8535fdd9 100644
DedicatedServer.LOGGER.info("Generating keypair");
this.a(MinecraftEncryption.b());
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index 709b28d78..4ba748ae2 100644
index 3c7537d013..ffaa425ec8 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -135,9 +135,9 @@ public abstract class Entity implements INamableTileEntity, ICommandListener {
@ -536,7 +536,7 @@ index 709b28d78..4ba748ae2 100644
public boolean impulse;
public int portalCooldown;
diff --git a/src/main/java/net/minecraft/server/EntityTypes.java b/src/main/java/net/minecraft/server/EntityTypes.java
index c6434ec37..17bfa356f 100644
index c6434ec371..17bfa356f1 100644
--- a/src/main/java/net/minecraft/server/EntityTypes.java
+++ b/src/main/java/net/minecraft/server/EntityTypes.java
@@ -2,6 +2,8 @@ package net.minecraft.server;
@ -560,7 +560,7 @@ index c6434ec37..17bfa356f 100644
+ // Paper end
}
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index ed77641e4..f381e23be 100644
index ed77641e46..f381e23beb 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -134,6 +134,8 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
@ -581,7 +581,7 @@ index ed77641e4..f381e23be 100644
this.world = new CraftWorld((WorldServer) this, gen, env);
this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index e28ce05fb..e659f3f33 100644
index e28ce05fb9..e659f3f339 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -750,6 +750,7 @@ public final class CraftServer implements Server {
@ -636,7 +636,7 @@ index e28ce05fb..e659f3f33 100644
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
index 4a998593b..e354245f7 100644
index 4a998593bd..e354245f73 100644
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -128,6 +128,14 @@ public class Main {
@ -655,7 +655,7 @@ index 4a998593b..e354245f7 100644
};
diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
index 95f7a9587..87bc8e2d9 100644
index 95f7a9587a..87bc8e2d9c 100644
--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
@@ -39,31 +39,31 @@ public class SpigotWorldConfig

View file

@ -1,4 +1,4 @@
From 523a76f0b9a204e83620100df735b2228c22306e Mon Sep 17 00:00:00 2001
From 5b1ddb18ea0397774eed24f27ab4d3fe2449141c Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 21 Jul 2018 16:55:04 -0400
Subject: [PATCH] Async Chunk Loading and Generation
@ -43,22 +43,64 @@ 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
@@ -372,4 +372,15 @@ public class PaperConfig {
@@ -372,4 +372,57 @@ public class PaperConfig {
}
}
}
+
+ // 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,10 +836,10 @@ 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 +1,478 @@
@@ -0,0 +1,477 @@
+/*
+ * This file is licensed under the MIT License (MIT).
+ *
@ -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
@@ -9,6 +9,7 @@ public class SchedulerBatch<K, T extends SchedulerTask<K, T>, R> {
@ -1421,16 +1462,18 @@ index d868149d1a..0d94b262ac 100644
public SchedulerBatch(Scheduler<K, T, R> scheduler) {
this.b = scheduler;
@@ -18,7 +19,9 @@ public class SchedulerBatch<K, T extends SchedulerTask<K, T>, R> {
@@ -18,8 +19,10 @@ public class SchedulerBatch<K, T extends SchedulerTask<K, T>, R> {
this.b.b();
}
+ 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;
@@ -27,6 +30,7 @@ public class SchedulerBatch<K, T extends SchedulerTask<K, T>, R> {
}
}
@ -1439,20 +1482,22 @@ index d868149d1a..0d94b262ac 100644
public CompletableFuture<R> a(K object) {
if (!this.c) {
throw new RuntimeException("Batch not properly started. Please use startBatch to create a new batch.");
@@ -42,7 +46,13 @@ public class SchedulerBatch<K, T extends SchedulerTask<K, T>, R> {
@@ -42,8 +46,14 @@ public class SchedulerBatch<K, T extends SchedulerTask<K, T>, R> {
}
}
+ 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) {
@@ -53,5 +63,6 @@ public class SchedulerBatch<K, T extends SchedulerTask<K, T>, R> {
this.c = false;
return this.b.c();