From 1ed50dade82437bf45fe74833d7e6ac2961842a2 Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Fri, 26 Jan 2024 21:34:40 +0100 Subject: [PATCH] Fix javadoc errors, remove unused classes --- patches/server/0007-ConcurrentUtil.patch | 12 +- patches/server/0008-CB-fixes.patch | 24 +- patches/server/0009-MC-Utils.patch | 17 +- patches/server/0014-Timings-v2.patch | 16 +- .../0060-Add-exception-reporting-event.patch | 6 +- ...e-CraftScheduler-Async-Task-Debugger.patch | 8 +- .../server/0135-Basic-PlayerProfile-API.patch | 6 +- .../0179-Improved-Async-Task-Scheduler.patch | 34 +- ...xception-in-entity-and-block-entity-.patch | 4 +- ...er-runTaskTimerAsynchronously-Plugin.patch | 4 +- ...ainThreadExecutor-to-BukkitScheduler.patch | 4 +- .../server/0991-Rewrite-chunk-system.patch | 1293 ++--------------- ...-incremental-chunk-and-player-saving.patch | 4 +- .../1008-Improved-Watchdog-Support.patch | 20 +- .../1021-Execute-chunk-tasks-mid-tick.patch | 4 +- .../server/1026-Collision-optimisations.patch | 10 +- ...nate-Current-redstone-implementation.patch | 4 +- patches/server/1033-Optimize-Hoppers.patch | 4 +- .../server/1037-Lag-compensation-ticks.patch | 4 +- 19 files changed, 191 insertions(+), 1287 deletions(-) diff --git a/patches/server/0007-ConcurrentUtil.patch b/patches/server/0007-ConcurrentUtil.patch index c2bb4af335..b5d70f9b38 100644 --- a/patches/server/0007-ConcurrentUtil.patch +++ b/patches/server/0007-ConcurrentUtil.patch @@ -3596,7 +3596,7 @@ index 0000000000000000000000000000000000000000..b71404be2c82f7db35272b367af861e9 +} diff --git a/src/main/java/ca/spottedleaf/concurrentutil/map/SWMRHashTable.java b/src/main/java/ca/spottedleaf/concurrentutil/map/SWMRHashTable.java new file mode 100644 -index 0000000000000000000000000000000000000000..a037bb57bedc0cde6b979f5c1f9669678fa7bd16 +index 0000000000000000000000000000000000000000..4289b984badd6f9167c86193454a630b9a40f9f5 --- /dev/null +++ b/src/main/java/ca/spottedleaf/concurrentutil/map/SWMRHashTable.java @@ -0,0 +1,1673 @@ @@ -3724,7 +3724,7 @@ index 0000000000000000000000000000000000000000..a037bb57bedc0cde6b979f5c1f966967 + /** + * Constructs this map with the specified capacity and load factor. + * @param capacity specified capacity, > 0 -+ * @param loadFactor specified load factor, > 0 && finite ++ * @param loadFactor specified load factor, > 0 and finite + */ + public SWMRHashTable(final int capacity, final float loadFactor) { + final int tableSize = getCapacityFor(capacity); @@ -3772,7 +3772,7 @@ index 0000000000000000000000000000000000000000..a037bb57bedc0cde6b979f5c1f966967 + * with the specified load factor. + * All of the specified map's entries are copied into this map. + * @param capacity specified capacity, > 0 -+ * @param loadFactor specified load factor, > 0 && finite ++ * @param loadFactor specified load factor, > 0 and finite + * @param other The specified map. + */ + public SWMRHashTable(final int capacity, final float loadFactor, final Map other) { @@ -5275,7 +5275,7 @@ index 0000000000000000000000000000000000000000..a037bb57bedc0cde6b979f5c1f966967 +} diff --git a/src/main/java/ca/spottedleaf/concurrentutil/map/SWMRLong2ObjectHashTable.java b/src/main/java/ca/spottedleaf/concurrentutil/map/SWMRLong2ObjectHashTable.java new file mode 100644 -index 0000000000000000000000000000000000000000..1e98f778ffa0a7bb00ebccaaa8bde075183e41f0 +index 0000000000000000000000000000000000000000..94fca3c9b31ca4e40688209e419e93320b0f7c34 --- /dev/null +++ b/src/main/java/ca/spottedleaf/concurrentutil/map/SWMRLong2ObjectHashTable.java @@ -0,0 +1,672 @@ @@ -5370,7 +5370,7 @@ index 0000000000000000000000000000000000000000..1e98f778ffa0a7bb00ebccaaa8bde075 + /** + * Constructs this map with the specified capacity and load factor. + * @param capacity specified capacity, > 0 -+ * @param loadFactor specified load factor, > 0 && finite ++ * @param loadFactor specified load factor, > 0 and finite + */ + public SWMRLong2ObjectHashTable(final int capacity, final float loadFactor) { + final int tableSize = getCapacityFor(capacity); @@ -5418,7 +5418,7 @@ index 0000000000000000000000000000000000000000..1e98f778ffa0a7bb00ebccaaa8bde075 + * with the specified load factor. + * All of the specified map's entries are copied into this map. + * @param capacity specified capacity, > 0 -+ * @param loadFactor specified load factor, > 0 && finite ++ * @param loadFactor specified load factor, > 0 and finite + * @param other The specified map. + */ + public SWMRLong2ObjectHashTable(final int capacity, final float loadFactor, final SWMRLong2ObjectHashTable other) { diff --git a/patches/server/0008-CB-fixes.patch b/patches/server/0008-CB-fixes.patch index 6d761eda94..c1b80fa115 100644 --- a/patches/server/0008-CB-fixes.patch +++ b/patches/server/0008-CB-fixes.patch @@ -84,7 +84,7 @@ index 809fdf2da78293391aa5c60c04f4ad652b152eec..955b0abd4019fc45df84719eee6bf413 + // Paper end } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index b5256eefb64808ae15bd622a8eccbe13454b4564..82d5c749a70f7a808c073263e861e09913c529d4 100644 +index 5f12fce84e0ec001dc43523753883a098434fcb6..d6a1b9bbf9737ed884ecf4af31e1521f46807405 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -2454,7 +2454,13 @@ public final class CraftServer implements Server { @@ -103,7 +103,7 @@ index b5256eefb64808ae15bd622a8eccbe13454b4564..82d5c749a70f7a808c073263e861e099 @Override diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index 686d6376322dab126393198da87e02827ec0fda2..5edad60f75a14b63ad704a68a3920f180ae82c40 100644 +index c8f0570b7d37a0c0bddb0a65c36fb32de584df8f..02060584892d630d91f58e864ed34656ee738572 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java @@ -122,6 +122,7 @@ public class Main { @@ -114,6 +114,26 @@ index 686d6376322dab126393198da87e02827ec0fda2..5edad60f75a14b63ad704a68a3920f18 this.acceptsAll(Main.asList("nogui"), "Disables the graphical console"); this.acceptsAll(Main.asList("nojline"), "Disables jline and emulates the vanilla console"); +diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +index 905adf97c0d1f0d1c774a6835a5dffcfea884e58..c017ce2ca1bc535795c958a2e509af2adf88efa9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java ++++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +@@ -26,6 +26,7 @@ import org.bukkit.scheduler.BukkitWorker; + + /** + * The fundamental concepts for this implementation: ++ *
    + *
  • Main thread owns {@link #head} and {@link #currentTick}, but it may be read from any thread
  • + *
  • Main thread exclusively controls {@link #temp} and {@link #pending}. + * They are never to be accessed outside of the main thread; alternatives exist to prevent locking.
  • +@@ -41,6 +42,7 @@ import org.bukkit.scheduler.BukkitWorker; + *
  • Sync tasks are only to be removed from runners on the main thread when coupled with a removal from pending and temp.
  • + *
  • Most of the design in this scheduler relies on queuing special tasks to perform any data changes on the main thread. + * When executed from inside a synchronous method, the scheduler will be updated before next execution by virtue of the frequent {@link #parsePending()} calls.
  • ++ *
+ */ + public class CraftScheduler implements BukkitScheduler { + diff --git a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java index 5ac25dab93fd4c9e9533c80d1ca3d93446d7a365..245ad120a36b6defca7e6889faae1ca5fc33d0c7 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java diff --git a/patches/server/0009-MC-Utils.patch b/patches/server/0009-MC-Utils.patch index 235159d382..26d49e05ae 100644 --- a/patches/server/0009-MC-Utils.patch +++ b/patches/server/0009-MC-Utils.patch @@ -2089,7 +2089,7 @@ index 0000000000000000000000000000000000000000..a743703502cea333bd4231b6557de50e +} diff --git a/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java b/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java new file mode 100644 -index 0000000000000000000000000000000000000000..653cbd10c7280f3aeeaaff712d083dde461da092 +index 0000000000000000000000000000000000000000..8066e27ff88454cb4bc8075d936e58a067dbe9b4 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java @@ -0,0 +1,71 @@ @@ -2098,7 +2098,7 @@ index 0000000000000000000000000000000000000000..653cbd10c7280f3aeeaaff712d083dde +import java.util.Collection; + +/** -+ * @author Spottedleaf ++ * @author Spottedleaf <Spottedleaf@users.noreply.github.com> + */ +public final class OptimizedSmallEnumSet> { + @@ -3482,10 +3482,10 @@ index 0000000000000000000000000000000000000000..cea9c098ade00ee87b8efc8164ab72f5 +} diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java new file mode 100644 -index 0000000000000000000000000000000000000000..6d60bb9f77198de3f0692c24b3b0ae085f3a80d0 +index 0000000000000000000000000000000000000000..7ae65f05eb2219a63a108728e4137b245775d08b --- /dev/null +++ b/src/main/java/io/papermc/paper/util/MCUtil.java -@@ -0,0 +1,533 @@ +@@ -0,0 +1,532 @@ +package io.papermc.paper.util; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -3800,7 +3800,6 @@ index 0000000000000000000000000000000000000000..6d60bb9f77198de3f0692c24b3b0ae08 + * Ensures the target code is running on the main thread + * @param reason + * @param run -+ * @return + */ + public static void ensureMain(String reason, Runnable run) { + if (!isMainThread()) { @@ -7934,10 +7933,10 @@ index bf46aa3bd46ffabe92d58aa45ea0dfe6c98d94aa..23b83f8e98d681895b4e23cda4f3d50f if (original instanceof CraftItemStack) { CraftItemStack stack = (CraftItemStack) original; diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -index 905adf97c0d1f0d1c774a6835a5dffcfea884e58..2b80ddb42c8e5fd32b37f89e894353167c8a698e 100644 +index c017ce2ca1bc535795c958a2e509af2adf88efa9..0f7c3a44acf3c59ae43605e573f9da7f7c594647 100644 --- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -@@ -44,6 +44,7 @@ import org.bukkit.scheduler.BukkitWorker; +@@ -46,6 +46,7 @@ import org.bukkit.scheduler.BukkitWorker; */ public class CraftScheduler implements BukkitScheduler { @@ -7945,7 +7944,7 @@ index 905adf97c0d1f0d1c774a6835a5dffcfea884e58..2b80ddb42c8e5fd32b37f89e89435316 /** * The start ID for the counter. */ -@@ -192,6 +193,11 @@ public class CraftScheduler implements BukkitScheduler { +@@ -194,6 +195,11 @@ public class CraftScheduler implements BukkitScheduler { this.runTaskTimer(plugin, (Object) task, delay, period); } @@ -7957,7 +7956,7 @@ index 905adf97c0d1f0d1c774a6835a5dffcfea884e58..2b80ddb42c8e5fd32b37f89e89435316 public BukkitTask runTaskTimer(Plugin plugin, Object runnable, long delay, long period) { CraftScheduler.validate(plugin, runnable); if (delay < 0L) { -@@ -415,13 +421,20 @@ public class CraftScheduler implements BukkitScheduler { +@@ -417,13 +423,20 @@ public class CraftScheduler implements BukkitScheduler { task.run(); task.timings.stopTiming(); // Spigot } catch (final Throwable throwable) { diff --git a/patches/server/0014-Timings-v2.patch b/patches/server/0014-Timings-v2.patch index 04e28eb9c6..c144a34a32 100644 --- a/patches/server/0014-Timings-v2.patch +++ b/patches/server/0014-Timings-v2.patch @@ -1838,7 +1838,7 @@ index f53e223e2412846b82298233459ec9953bc0a63e..87e7071a381540be3b1db55f5d606e9e public Player.Spigot spigot() diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -index 2b80ddb42c8e5fd32b37f89e894353167c8a698e..bd1057681d0c7470c497b873ff18abf03a0a6a66 100644 +index 0f7c3a44acf3c59ae43605e573f9da7f7c594647..a3ccc2da0927cc49e5fcfbd863e648ad0f25cc0d 100644 --- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java @@ -1,5 +1,6 @@ @@ -1848,7 +1848,7 @@ index 2b80ddb42c8e5fd32b37f89e894353167c8a698e..bd1057681d0c7470c497b873ff18abf0 import com.google.common.base.Preconditions; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.util.ArrayList; -@@ -194,7 +195,8 @@ public class CraftScheduler implements BukkitScheduler { +@@ -196,7 +197,8 @@ public class CraftScheduler implements BukkitScheduler { } public BukkitTask scheduleInternalTask(Runnable run, int delay, String taskName) { @@ -1858,7 +1858,7 @@ index 2b80ddb42c8e5fd32b37f89e894353167c8a698e..bd1057681d0c7470c497b873ff18abf0 return handle(task, delay); } -@@ -275,7 +277,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -277,7 +279,7 @@ public class CraftScheduler implements BukkitScheduler { } return false; } @@ -1867,7 +1867,7 @@ index 2b80ddb42c8e5fd32b37f89e894353167c8a698e..bd1057681d0c7470c497b873ff18abf0 this.handle(task, 0L); for (CraftTask taskPending = this.head.getNext(); taskPending != null; taskPending = taskPending.getNext()) { if (taskPending == task) { -@@ -310,7 +312,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -312,7 +314,7 @@ public class CraftScheduler implements BukkitScheduler { } } } @@ -1876,7 +1876,7 @@ index 2b80ddb42c8e5fd32b37f89e894353167c8a698e..bd1057681d0c7470c497b873ff18abf0 this.handle(task, 0L); for (CraftTask taskPending = this.head.getNext(); taskPending != null; taskPending = taskPending.getNext()) { if (taskPending == task) { -@@ -417,9 +419,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -419,9 +421,7 @@ public class CraftScheduler implements BukkitScheduler { if (task.isSync()) { this.currentTask = task; try { @@ -1886,7 +1886,7 @@ index 2b80ddb42c8e5fd32b37f89e894353167c8a698e..bd1057681d0c7470c497b873ff18abf0 } catch (final Throwable throwable) { // Paper start String msg = String.format( -@@ -453,8 +453,10 @@ public class CraftScheduler implements BukkitScheduler { +@@ -455,8 +455,10 @@ public class CraftScheduler implements BukkitScheduler { this.runners.remove(task.getTaskId()); } } @@ -1897,7 +1897,7 @@ index 2b80ddb42c8e5fd32b37f89e894353167c8a698e..bd1057681d0c7470c497b873ff18abf0 this.debugHead = this.debugHead.getNextHead(currentTick); } -@@ -491,6 +493,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -493,6 +495,7 @@ public class CraftScheduler implements BukkitScheduler { } private void parsePending() { @@ -1905,7 +1905,7 @@ index 2b80ddb42c8e5fd32b37f89e894353167c8a698e..bd1057681d0c7470c497b873ff18abf0 CraftTask head = this.head; CraftTask task = head.getNext(); CraftTask lastTask = head; -@@ -509,6 +512,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -511,6 +514,7 @@ public class CraftScheduler implements BukkitScheduler { task.setNext(null); } this.head = lastTask; diff --git a/patches/server/0060-Add-exception-reporting-event.patch b/patches/server/0060-Add-exception-reporting-event.patch index 9406646b7d..a5a8a798ec 100644 --- a/patches/server/0060-Add-exception-reporting-event.patch +++ b/patches/server/0060-Add-exception-reporting-event.patch @@ -192,10 +192,10 @@ index d9daf07132c46548964a75588b69d7a74680e917..66994aa135037919219e0bbcabe7de9f try { filechannel.close(); diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -index bd1057681d0c7470c497b873ff18abf03a0a6a66..f39c836970572fe2e29e794a6af35332af8f7424 100644 +index a3ccc2da0927cc49e5fcfbd863e648ad0f25cc0d..dc7872afbdd06eb976bee6aee56a40b44084c24a 100644 --- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -@@ -434,6 +434,8 @@ public class CraftScheduler implements BukkitScheduler { +@@ -436,6 +436,8 @@ public class CraftScheduler implements BukkitScheduler { msg, throwable); } @@ -204,7 +204,7 @@ index bd1057681d0c7470c497b873ff18abf03a0a6a66..f39c836970572fe2e29e794a6af35332 // Paper end } finally { this.currentTask = null; -@@ -441,7 +443,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -443,7 +445,7 @@ public class CraftScheduler implements BukkitScheduler { this.parsePending(); } else { this.debugTail = this.debugTail.setNext(new CraftAsyncDebugger(currentTick + CraftScheduler.RECENT_TICKS, task.getOwner(), task.getTaskClass())); diff --git a/patches/server/0125-Remove-CraftScheduler-Async-Task-Debugger.patch b/patches/server/0125-Remove-CraftScheduler-Async-Task-Debugger.patch index 94bbaced71..721f748db6 100644 --- a/patches/server/0125-Remove-CraftScheduler-Async-Task-Debugger.patch +++ b/patches/server/0125-Remove-CraftScheduler-Async-Task-Debugger.patch @@ -9,10 +9,10 @@ One report of a suspected memory leak with the system. This adds additional overhead to asynchronous task dispatching diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -index f39c836970572fe2e29e794a6af35332af8f7424..b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2 100644 +index dc7872afbdd06eb976bee6aee56a40b44084c24a..af3997e47aff9c43dc5019f1b0267effe1df5205 100644 --- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -@@ -442,7 +442,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -444,7 +444,7 @@ public class CraftScheduler implements BukkitScheduler { } this.parsePending(); } else { @@ -21,7 +21,7 @@ index f39c836970572fe2e29e794a6af35332af8f7424..b1e61ce608eeacdbb70849c1e3e39daf this.executor.execute(new com.destroystokyo.paper.ServerSchedulerReportingWrapper(task)); // Paper // We don't need to parse pending // (async tasks must live with race-conditions if they attempt to cancel between these few lines of code) -@@ -459,7 +459,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -461,7 +461,7 @@ public class CraftScheduler implements BukkitScheduler { this.pending.addAll(temp); temp.clear(); MinecraftTimings.bukkitSchedulerFinishTimer.stopTiming(); // Paper @@ -30,7 +30,7 @@ index f39c836970572fe2e29e794a6af35332af8f7424..b1e61ce608eeacdbb70849c1e3e39daf } private void addTask(final CraftTask task) { -@@ -523,10 +523,15 @@ public class CraftScheduler implements BukkitScheduler { +@@ -525,10 +525,15 @@ public class CraftScheduler implements BukkitScheduler { @Override public String toString() { diff --git a/patches/server/0135-Basic-PlayerProfile-API.patch b/patches/server/0135-Basic-PlayerProfile-API.patch index 1793add7cc..467af240c4 100644 --- a/patches/server/0135-Basic-PlayerProfile-API.patch +++ b/patches/server/0135-Basic-PlayerProfile-API.patch @@ -546,7 +546,7 @@ index 0000000000000000000000000000000000000000..7ac27392a8647ef7d0dc78efe78703e9 + @NotNull GameProfile buildGameProfile(); +} diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java -index 6d60bb9f77198de3f0692c24b3b0ae085f3a80d0..e5a7a77fda0c7e81c8535ef8cfaca2222b0d79fb 100644 +index 7ae65f05eb2219a63a108728e4137b245775d08b..b08190d604f7f95f771a4da115cc50c38c5f1691 100644 --- a/src/main/java/io/papermc/paper/util/MCUtil.java +++ b/src/main/java/io/papermc/paper/util/MCUtil.java @@ -1,5 +1,7 @@ @@ -565,7 +565,7 @@ index 6d60bb9f77198de3f0692c24b3b0ae085f3a80d0..e5a7a77fda0c7e81c8535ef8cfaca222 import org.bukkit.Location; import org.bukkit.block.BlockFace; import org.bukkit.craftbukkit.CraftWorld; -@@ -361,6 +364,10 @@ public final class MCUtil { +@@ -360,6 +363,10 @@ public final class MCUtil { return run.get(); } @@ -612,7 +612,7 @@ index adb472c175cc6f6ced7075a37423d6c898fd5ccb..1ec0f3a7148c2f412421772f6e1dff0b String s1 = name.toLowerCase(Locale.ROOT); GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByName.get(s1); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 883c053baea5968a978f1619ebb170647fe15eef..22f5ff1e6f03fc46eb05eaf95ca6f8e2bac1f894 100644 +index e7ad8c2d40568a243b6d3b2a28065083802afe5d..5c9415a5771191b7e3fec02f5a9ad569e735c4aa 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -260,6 +260,9 @@ import org.yaml.snakeyaml.error.MarkedYAMLException; diff --git a/patches/server/0179-Improved-Async-Task-Scheduler.patch b/patches/server/0179-Improved-Async-Task-Scheduler.patch index e8a6c646f6..3ce3a754e3 100644 --- a/patches/server/0179-Improved-Async-Task-Scheduler.patch +++ b/patches/server/0179-Improved-Async-Task-Scheduler.patch @@ -159,10 +159,10 @@ index 0000000000000000000000000000000000000000..3c1992e212a6d6f1db4d5b807b38d719 + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc163a4a0f 100644 +index af3997e47aff9c43dc5019f1b0267effe1df5205..c6ce8ed5fa73ee6221332083b3376b30bfe61bd0 100644 --- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -@@ -75,7 +75,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -77,7 +77,7 @@ public class CraftScheduler implements BukkitScheduler { /** * Main thread logic only */ @@ -171,7 +171,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc new Comparator() { @Override public int compare(final CraftTask o1, final CraftTask o2) { -@@ -92,12 +92,13 @@ public class CraftScheduler implements BukkitScheduler { +@@ -94,12 +94,13 @@ public class CraftScheduler implements BukkitScheduler { /** * These are tasks that are currently active. It's provided for 'viewing' the current state. */ @@ -187,7 +187,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc private final Executor executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %d").build()); private CraftAsyncDebugger debugHead = new CraftAsyncDebugger(-1, null, null) { @Override -@@ -106,12 +107,31 @@ public class CraftScheduler implements BukkitScheduler { +@@ -108,12 +109,31 @@ public class CraftScheduler implements BukkitScheduler { } }; private CraftAsyncDebugger debugTail = this.debugHead; @@ -219,7 +219,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc @Override public int scheduleSyncDelayedTask(final Plugin plugin, final Runnable task) { return this.scheduleSyncDelayedTask(plugin, task, 0L); -@@ -234,7 +254,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -236,7 +256,7 @@ public class CraftScheduler implements BukkitScheduler { } else if (period < CraftTask.NO_REPEATING) { period = CraftTask.NO_REPEATING; } @@ -228,7 +228,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc } @Override -@@ -250,6 +270,11 @@ public class CraftScheduler implements BukkitScheduler { +@@ -252,6 +272,11 @@ public class CraftScheduler implements BukkitScheduler { if (taskId <= 0) { return; } @@ -240,7 +240,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc CraftTask task = this.runners.get(taskId); if (task != null) { task.cancel0(); -@@ -292,6 +317,11 @@ public class CraftScheduler implements BukkitScheduler { +@@ -294,6 +319,11 @@ public class CraftScheduler implements BukkitScheduler { @Override public void cancelTasks(final Plugin plugin) { Preconditions.checkArgument(plugin != null, "Cannot cancel tasks of null plugin"); @@ -252,7 +252,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc final CraftTask task = new CraftTask( new Runnable() { @Override -@@ -331,6 +361,13 @@ public class CraftScheduler implements BukkitScheduler { +@@ -333,6 +363,13 @@ public class CraftScheduler implements BukkitScheduler { @Override public boolean isCurrentlyRunning(final int taskId) { @@ -266,7 +266,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc final CraftTask task = this.runners.get(taskId); if (task == null) { return false; -@@ -349,6 +386,11 @@ public class CraftScheduler implements BukkitScheduler { +@@ -351,6 +388,11 @@ public class CraftScheduler implements BukkitScheduler { if (taskId <= 0) { return false; } @@ -278,7 +278,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc for (CraftTask task = this.head.getNext(); task != null; task = task.getNext()) { if (task.getTaskId() == taskId) { return task.getPeriod() >= CraftTask.NO_REPEATING; // The task will run -@@ -360,6 +402,12 @@ public class CraftScheduler implements BukkitScheduler { +@@ -362,6 +404,12 @@ public class CraftScheduler implements BukkitScheduler { @Override public List getActiveWorkers() { @@ -291,7 +291,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc final ArrayList workers = new ArrayList(); for (final CraftTask taskObj : this.runners.values()) { // Iterator will be a best-effort (may fail to grab very new values) if called from an async thread -@@ -397,6 +445,11 @@ public class CraftScheduler implements BukkitScheduler { +@@ -399,6 +447,11 @@ public class CraftScheduler implements BukkitScheduler { pending.add(task); } } @@ -303,7 +303,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc return pending; } -@@ -404,6 +457,11 @@ public class CraftScheduler implements BukkitScheduler { +@@ -406,6 +459,11 @@ public class CraftScheduler implements BukkitScheduler { * This method is designed to never block or wait for locks; an immediate execution of all current tasks. */ public void mainThreadHeartbeat(final int currentTick) { @@ -315,7 +315,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc this.currentTick = currentTick; final List temp = this.temp; this.parsePending(); -@@ -443,7 +501,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -445,7 +503,7 @@ public class CraftScheduler implements BukkitScheduler { this.parsePending(); } else { // this.debugTail = this.debugTail.setNext(new CraftAsyncDebugger(currentTick + CraftScheduler.RECENT_TICKS, task.getOwner(), task.getTaskClass())); // Paper @@ -324,7 +324,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc // We don't need to parse pending // (async tasks must live with race-conditions if they attempt to cancel between these few lines of code) } -@@ -462,7 +520,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -464,7 +522,7 @@ public class CraftScheduler implements BukkitScheduler { //this.debugHead = this.debugHead.getNextHead(currentTick); // Paper } @@ -333,7 +333,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc final AtomicReference tail = this.tail; CraftTask tailTask = tail.get(); while (!tail.compareAndSet(tailTask, task)) { -@@ -471,7 +529,13 @@ public class CraftScheduler implements BukkitScheduler { +@@ -473,7 +531,13 @@ public class CraftScheduler implements BukkitScheduler { tailTask.setNext(task); } @@ -348,7 +348,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc task.setNextRun(this.currentTick + delay); this.addTask(task); return task; -@@ -494,8 +558,8 @@ public class CraftScheduler implements BukkitScheduler { +@@ -496,8 +560,8 @@ public class CraftScheduler implements BukkitScheduler { return id; } @@ -359,7 +359,7 @@ index b1e61ce608eeacdbb70849c1e3e39daf37b3f2a2..215310984100722757d9dd38182f7cbc CraftTask head = this.head; CraftTask task = head.getNext(); CraftTask lastTask = head; -@@ -514,7 +578,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -516,7 +580,7 @@ public class CraftScheduler implements BukkitScheduler { task.setNext(null); } this.head = lastTask; diff --git a/patches/server/0260-Catch-JsonParseException-in-entity-and-block-entity-.patch b/patches/server/0260-Catch-JsonParseException-in-entity-and-block-entity-.patch index 8ff9ed7c54..eceea63d83 100644 --- a/patches/server/0260-Catch-JsonParseException-in-entity-and-block-entity-.patch +++ b/patches/server/0260-Catch-JsonParseException-in-entity-and-block-entity-.patch @@ -13,10 +13,10 @@ Shulkers) may need to be changed in order for it to re-save properly No more crashing though. diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java -index e5a7a77fda0c7e81c8535ef8cfaca2222b0d79fb..ff8a983e23bcf73ec3f7dd98c48640183647ad4d 100644 +index b08190d604f7f95f771a4da115cc50c38c5f1691..8235d23dc9778b9444288d58ffdb5d515df56bf9 100644 --- a/src/main/java/io/papermc/paper/util/MCUtil.java +++ b/src/main/java/io/papermc/paper/util/MCUtil.java -@@ -537,4 +537,19 @@ public final class MCUtil { +@@ -536,4 +536,19 @@ public final class MCUtil { public static int getTicketLevelFor(net.minecraft.world.level.chunk.ChunkStatus status) { return net.minecraft.server.level.ChunkMap.MAX_VIEW_DISTANCE + net.minecraft.world.level.chunk.ChunkStatus.getDistance(status); } diff --git a/patches/server/0381-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch b/patches/server/0381-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch index 79528977b9..4175cae3b0 100644 --- a/patches/server/0381-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch +++ b/patches/server/0381-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch @@ -7,10 +7,10 @@ Subject: [PATCH] Fix CraftScheduler#runTaskTimerAsynchronously(Plugin, diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -index 215310984100722757d9dd38182f7cbc163a4a0f..acb2af336184c0215c409c748b56fddd8d1fb4be 100644 +index c6ce8ed5fa73ee6221332083b3376b30bfe61bd0..71a147df3bcbd5bb82934da3e6e14326746cf2e3 100644 --- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -@@ -196,7 +196,7 @@ public class CraftScheduler implements BukkitScheduler { +@@ -198,7 +198,7 @@ public class CraftScheduler implements BukkitScheduler { @Override public void runTaskTimerAsynchronously(Plugin plugin, Consumer task, long delay, long period) throws IllegalArgumentException { diff --git a/patches/server/0515-Add-getMainThreadExecutor-to-BukkitScheduler.patch b/patches/server/0515-Add-getMainThreadExecutor-to-BukkitScheduler.patch index fae7ff0de6..57d54a0434 100644 --- a/patches/server/0515-Add-getMainThreadExecutor-to-BukkitScheduler.patch +++ b/patches/server/0515-Add-getMainThreadExecutor-to-BukkitScheduler.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add getMainThreadExecutor to BukkitScheduler diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -index acb2af336184c0215c409c748b56fddd8d1fb4be..700932b65e4fda560d684b0aa079bcee3923f73e 100644 +index 71a147df3bcbd5bb82934da3e6e14326746cf2e3..e85b9bb3f9c225d289a4959921970b9963881199 100644 --- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -@@ -651,4 +651,15 @@ public class CraftScheduler implements BukkitScheduler { +@@ -653,4 +653,15 @@ public class CraftScheduler implements BukkitScheduler { public BukkitTask runTaskTimerAsynchronously(Plugin plugin, BukkitRunnable task, long delay, long period) throws IllegalArgumentException { throw new UnsupportedOperationException("Use BukkitRunnable#runTaskTimerAsynchronously(Plugin, long, long)"); } diff --git a/patches/server/0991-Rewrite-chunk-system.patch b/patches/server/0991-Rewrite-chunk-system.patch index bf832ddd94..7109499cae 100644 --- a/patches/server/0991-Rewrite-chunk-system.patch +++ b/patches/server/0991-Rewrite-chunk-system.patch @@ -961,1121 +961,6 @@ index 2f0d9b953802dee821cfde82d22b0567cce8ee91..22687667ec69a954261e55e59261286a } public static Timing getTickList(ServerLevel worldserver, String timingsType) { -diff --git a/src/main/java/com/destroystokyo/paper/io/IOUtil.java b/src/main/java/com/destroystokyo/paper/io/IOUtil.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e064f96c90afd1a4890060baa055cfd0469b6a6f ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/IOUtil.java -@@ -0,0 +1,63 @@ -+package com.destroystokyo.paper.io; -+ -+import org.bukkit.Bukkit; -+ -+@Deprecated(forRemoval = true) -+public final class IOUtil { -+ -+ /* Copied from concrete or concurrentutil */ -+ -+ public static long getCoordinateKey(final int x, final int z) { -+ return ((long)z << 32) | (x & 0xFFFFFFFFL); -+ } -+ -+ public static int getCoordinateX(final long key) { -+ return (int)key; -+ } -+ -+ public static int getCoordinateZ(final long key) { -+ return (int)(key >>> 32); -+ } -+ -+ public static int getRegionCoordinate(final int chunkCoordinate) { -+ return chunkCoordinate >> 5; -+ } -+ -+ public static int getChunkInRegion(final int chunkCoordinate) { -+ return chunkCoordinate & 31; -+ } -+ -+ public static String genericToString(final Object object) { -+ return object == null ? "null" : object.getClass().getName() + ":" + object.toString(); -+ } -+ -+ public static T notNull(final T obj) { -+ if (obj == null) { -+ throw new NullPointerException(); -+ } -+ return obj; -+ } -+ -+ public static T notNull(final T obj, final String msgIfNull) { -+ if (obj == null) { -+ throw new NullPointerException(msgIfNull); -+ } -+ return obj; -+ } -+ -+ public static void arrayBounds(final int off, final int len, final int arrayLength, final String msgPrefix) { -+ if (off < 0 || len < 0 || (arrayLength - off) < len) { -+ throw new ArrayIndexOutOfBoundsException(msgPrefix + ": off: " + off + ", len: " + len + ", array length: " + arrayLength); -+ } -+ } -+ -+ public static int getPriorityForCurrentThread() { -+ return Bukkit.isPrimaryThread() ? PrioritizedTaskQueue.HIGHEST_PRIORITY : PrioritizedTaskQueue.NORMAL_PRIORITY; -+ } -+ -+ @SuppressWarnings("unchecked") -+ public static void rethrow(final Throwable throwable) throws T { -+ throw (T)throwable; -+ } -+ -+} -diff --git a/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java b/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f2c27e0ac65be4b75c1d86ef6fd45fdb538d96ac ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java -@@ -0,0 +1,474 @@ -+package com.destroystokyo.paper.io; -+ -+import com.mojang.logging.LogUtils; -+import net.minecraft.nbt.CompoundTag; -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.world.level.ChunkPos; -+import net.minecraft.world.level.chunk.storage.RegionFile; -+import org.slf4j.Logger; -+ -+import java.io.IOException; -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.ConcurrentHashMap; -+import java.util.concurrent.atomic.AtomicLong; -+import java.util.function.Consumer; -+import java.util.function.Function; -+ -+/** -+ * Prioritized singleton thread responsible for all chunk IO that occurs in a minecraft server. -+ * -+ *

-+ * Singleton access: {@link Holder#INSTANCE} -+ *

-+ * -+ *

-+ * All functions provided are MT-Safe, however certain ordering constraints are (but not enforced): -+ *

  • -+ * Chunk saves may not occur for unloaded chunks. -+ *
  • -+ *
  • -+ * Tasks must be scheduled on the main thread. -+ *
  • -+ *

    -+ * -+ * @see Holder#INSTANCE -+ * @see #scheduleSave(ServerLevel, int, int, CompoundTag, CompoundTag, int) -+ * @see #loadChunkDataAsync(ServerLevel, int, int, int, Consumer, boolean, boolean, boolean) -+ * @deprecated -+ */ -+@Deprecated(forRemoval = true) -+public final class PaperFileIOThread extends QueueExecutorThread { -+ -+ public static final Logger LOGGER = LogUtils.getLogger(); -+ public static final CompoundTag FAILURE_VALUE = new CompoundTag(); -+ -+ public static final class Holder { -+ -+ public static final PaperFileIOThread INSTANCE = new PaperFileIOThread(); -+ -+ static { -+ // Paper - fail hard on usage -+ } -+ } -+ -+ private final AtomicLong writeCounter = new AtomicLong(); -+ -+ private PaperFileIOThread() { -+ super(new PrioritizedTaskQueue<>(), (int)(1.0e6)); // 1.0ms spinwait time -+ this.setName("Paper RegionFile IO Thread"); -+ this.setPriority(Thread.NORM_PRIORITY - 1); // we keep priority close to normal because threads can wait on us -+ this.setUncaughtExceptionHandler((final Thread unused, final Throwable thr) -> { -+ LOGGER.error("Uncaught exception thrown from IO thread, report this!", thr); -+ }); -+ } -+ -+ /* run() is implemented by superclass */ -+ -+ /* -+ * -+ * IO thread will perform reads before writes -+ * -+ * How reads/writes are scheduled: -+ * -+ * If read in progress while scheduling write, ignore read and schedule write -+ * If read in progress while scheduling read (no write in progress), chain the read task -+ * -+ * -+ * If write in progress while scheduling read, use the pending write data and ret immediately -+ * If write in progress while scheduling write (ignore read in progress), overwrite the write in progress data -+ * -+ * This allows the reads and writes to act as if they occur synchronously to the thread scheduling them, however -+ * it fails to properly propagate write failures. When writes fail the data is kept so future reads will actually -+ * read the failed write data. This should hopefully act as a way to prevent data loss for spurious fails for writing data. -+ * -+ */ -+ -+ /** -+ * Attempts to bump the priority of all IO tasks for the given chunk coordinates. This has no effect if no tasks are queued. -+ * @param world Chunk's world -+ * @param chunkX Chunk's x coordinate -+ * @param chunkZ Chunk's z coordinate -+ * @param priority Priority level to try to bump to -+ */ -+ public void bumpPriority(final ServerLevel world, final int chunkX, final int chunkZ, final int priority) { -+ throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage -+ } -+ -+ public CompoundTag getPendingWrite(final ServerLevel world, final int chunkX, final int chunkZ, final boolean poiData) { -+ // Paper start - rewrite chunk system -+ return io.papermc.paper.chunk.system.io.RegionFileIOThread.getPendingWrite( -+ world, chunkX, chunkZ, poiData ? io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA : -+ io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA -+ ); -+ // Paper end - rewrite chunk system -+ } -+ -+ /** -+ * Sets the priority of all IO tasks for the given chunk coordinates. This has no effect if no tasks are queued. -+ * @param world Chunk's world -+ * @param chunkX Chunk's x coordinate -+ * @param chunkZ Chunk's z coordinate -+ * @param priority Priority level to set to -+ */ -+ public void setPriority(final ServerLevel world, final int chunkX, final int chunkZ, final int priority) { -+ throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage -+ } -+ -+ /** -+ * Schedules the chunk data to be written asynchronously. -+ *

    -+ * Impl notes: -+ *

    -+ *
  • -+ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means -+ * saves must be scheduled before a chunk is unloaded. -+ *
  • -+ *
  • -+ * Writes may be called concurrently, although only the "later" write will go through. -+ *
  • -+ * @param world Chunk's world -+ * @param chunkX Chunk's x coordinate -+ * @param chunkZ Chunk's z coordinate -+ * @param poiData Chunk point of interest data. If {@code null}, then no poi data is saved. -+ * @param chunkData Chunk data. If {@code null}, then no chunk data is saved. -+ * @param priority Priority level for this task. See {@link PrioritizedTaskQueue} -+ * @throws IllegalArgumentException If both {@code poiData} and {@code chunkData} are {@code null}. -+ * @throws IllegalStateException If the file io thread has shutdown. -+ */ -+ public void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, -+ final CompoundTag poiData, final CompoundTag chunkData, -+ final int priority) throws IllegalArgumentException { -+ throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage -+ } -+ -+ private void scheduleWrite(final ChunkDataController dataController, final ServerLevel world, -+ final int chunkX, final int chunkZ, final CompoundTag data, final int priority, final long writeCounter) { -+ throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage -+ } -+ -+ /** -+ * Same as {@link #loadChunkDataAsync(ServerLevel, int, int, int, Consumer, boolean, boolean, boolean)}, except this function returns -+ * a {@link CompletableFuture} which is potentially completed ASYNCHRONOUSLY ON THE FILE IO THREAD when the load task -+ * has completed. -+ *

    -+ * Note that if the chunk fails to load the returned future is completed with {@code null}. -+ *

    -+ */ -+ public CompletableFuture loadChunkDataAsyncFuture(final ServerLevel world, final int chunkX, final int chunkZ, -+ final int priority, final boolean readPoiData, final boolean readChunkData, -+ final boolean intendingToBlock) { -+ final CompletableFuture future = new CompletableFuture<>(); -+ this.loadChunkDataAsync(world, chunkX, chunkZ, priority, future::complete, readPoiData, readChunkData, intendingToBlock); -+ return future; -+ } -+ -+ /** -+ * Schedules a load to be executed asynchronously. -+ *

    -+ * Impl notes: -+ *

    -+ *
  • -+ * If a chunk fails to load, the {@code onComplete} parameter is completed with {@code null}. -+ *
  • -+ *
  • -+ * It is possible for the {@code onComplete} parameter to be given {@link ChunkData} containing data -+ * this call did not request. -+ *
  • -+ *
  • -+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may -+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of -+ * data is undefined behaviour, and can cause deadlock. -+ *
  • -+ * @param world Chunk's world -+ * @param chunkX Chunk's x coordinate -+ * @param chunkZ Chunk's z coordinate -+ * @param priority Priority level for this task. See {@link PrioritizedTaskQueue} -+ * @param onComplete Consumer to execute once this task has completed -+ * @param readPoiData Whether to read point of interest data. If {@code false}, the {@code NBTTagCompound} will be {@code null}. -+ * @param readChunkData Whether to read chunk data. If {@code false}, the {@code NBTTagCompound} will be {@code null}. -+ * @return The {@link PrioritizedTaskQueue.PrioritizedTask} associated with this task. Note that this task does not support -+ * cancellation. -+ */ -+ public void loadChunkDataAsync(final ServerLevel world, final int chunkX, final int chunkZ, -+ final int priority, final Consumer onComplete, -+ final boolean readPoiData, final boolean readChunkData, -+ final boolean intendingToBlock) { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalArgumentException("Invalid priority: " + priority); -+ } -+ -+ if (!(readPoiData | readChunkData)) { -+ throw new IllegalArgumentException("Must read chunk data or poi data"); -+ } -+ -+ final ChunkData complete = new ChunkData(); -+ // Paper start - rewrite chunk system -+ final java.util.List types = new java.util.ArrayList<>(); -+ if (readPoiData) { -+ types.add(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA); -+ } -+ if (readChunkData) { -+ types.add(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA); -+ } -+ final ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority newPriority; -+ switch (priority) { -+ case PrioritizedTaskQueue.HIGHEST_PRIORITY -> newPriority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.BLOCKING; -+ case PrioritizedTaskQueue.HIGHER_PRIORITY -> newPriority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHEST; -+ case PrioritizedTaskQueue.HIGH_PRIORITY -> newPriority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGH; -+ case PrioritizedTaskQueue.NORMAL_PRIORITY -> newPriority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL; -+ case PrioritizedTaskQueue.LOW_PRIORITY -> newPriority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.LOW; -+ case PrioritizedTaskQueue.LOWEST_PRIORITY -> newPriority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.IDLE; -+ default -> throw new IllegalStateException("Legacy priority " + priority + " should be valid"); -+ } -+ final Consumer transformComplete = (io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileData data) -> { -+ if (readPoiData) { -+ if (data.getThrowable(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA) != null) { -+ complete.poiData = FAILURE_VALUE; -+ } else { -+ complete.poiData = data.getData(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA); -+ } -+ } -+ -+ if (readChunkData) { -+ if (data.getThrowable(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA) != null) { -+ complete.chunkData = FAILURE_VALUE; -+ } else { -+ complete.chunkData = data.getData(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA); -+ } -+ } -+ -+ onComplete.accept(complete); -+ }; -+ io.papermc.paper.chunk.system.io.RegionFileIOThread.loadChunkData(world, chunkX, chunkZ, transformComplete, intendingToBlock, newPriority, types.toArray(new io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType[0])); -+ // Paper end - rewrite chunk system -+ -+ } -+ -+ // Note: the onComplete may be called asynchronously or synchronously here. -+ private void scheduleRead(final ChunkDataController dataController, final ServerLevel world, -+ final int chunkX, final int chunkZ, final Consumer onComplete, final int priority, -+ final boolean intendingToBlock) { -+ throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage -+ } -+ -+ /** -+ * Same as {@link #loadChunkDataAsync(ServerLevel, int, int, int, Consumer, boolean, boolean, boolean)}, except this function returns -+ * the {@link ChunkData} associated with the specified chunk when the task is complete. -+ * @return The chunk data, or {@code null} if the chunk failed to load. -+ */ -+ public ChunkData loadChunkData(final ServerLevel world, final int chunkX, final int chunkZ, final int priority, -+ final boolean readPoiData, final boolean readChunkData) { -+ return this.loadChunkDataAsyncFuture(world, chunkX, chunkZ, priority, readPoiData, readChunkData, true).join(); -+ } -+ -+ /** -+ * Schedules the given task at the specified priority to be executed on the IO thread. -+ *

    -+ * Internal api. Do not use. -+ *

    -+ */ -+ public void runTask(final int priority, final Runnable runnable) { -+ throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage -+ } -+ -+ static final class GeneralTask extends PrioritizedTaskQueue.PrioritizedTask implements Runnable { -+ -+ private final Runnable run; -+ -+ public GeneralTask(final int priority, final Runnable run) { -+ super(priority); -+ this.run = IOUtil.notNull(run, "Task may not be null"); -+ } -+ -+ @Override -+ public void run() { -+ try { -+ this.run.run(); -+ } catch (final Throwable throwable) { -+ if (throwable instanceof ThreadDeath) { -+ throw (ThreadDeath)throwable; -+ } -+ LOGGER.error("Failed to execute general task on IO thread " + IOUtil.genericToString(this.run), throwable); -+ } -+ } -+ } -+ -+ public static final class ChunkData { -+ -+ public CompoundTag poiData; -+ public CompoundTag chunkData; -+ -+ public ChunkData() {} -+ -+ public ChunkData(final CompoundTag poiData, final CompoundTag chunkData) { -+ this.poiData = poiData; -+ this.chunkData = chunkData; -+ } -+ } -+ -+ public static abstract class ChunkDataController { -+ -+ // ConcurrentHashMap synchronizes per chain, so reduce the chance of task's hashes colliding. -+ public final ConcurrentHashMap tasks = new ConcurrentHashMap<>(64, 0.5f); -+ -+ public abstract void writeData(final int x, final int z, final CompoundTag compound) throws IOException; -+ public abstract CompoundTag readData(final int x, final int z) throws IOException; -+ -+ public abstract T computeForRegionFile(final int chunkX, final int chunkZ, final Function function); -+ public abstract T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function function); -+ -+ public static final class InProgressWrite { -+ public long writeCounter; -+ public CompoundTag data; -+ } -+ -+ public static final class InProgressRead { -+ public final CompletableFuture readFuture = new CompletableFuture<>(); -+ } -+ } -+ -+ public static final class ChunkDataTask extends PrioritizedTaskQueue.PrioritizedTask implements Runnable { -+ -+ public ChunkDataController.InProgressWrite inProgressWrite; -+ public ChunkDataController.InProgressRead inProgressRead; -+ -+ private final ServerLevel world; -+ private final int x; -+ private final int z; -+ private final ChunkDataController taskController; -+ -+ public ChunkDataTask(final int priority, final ServerLevel world, final int x, final int z, final ChunkDataController taskController) { -+ super(priority); -+ this.world = world; -+ this.x = x; -+ this.z = z; -+ this.taskController = taskController; -+ } -+ -+ @Override -+ public String toString() { -+ return "Task for world: '" + this.world.getWorld().getName() + "' at " + this.x + "," + this.z + -+ " poi: " + (this.taskController == null) + ", hash: " + this.hashCode(); // Paper - TODO rewrite chunk system -+ } -+ -+ /* -+ * -+ * IO thread will perform reads before writes -+ * -+ * How reads/writes are scheduled: -+ * -+ * If read in progress while scheduling write, ignore read and schedule write -+ * If read in progress while scheduling read (no write in progress), chain the read task -+ * -+ * -+ * If write in progress while scheduling read, use the pending write data and ret immediately -+ * If write in progress while scheduling write (ignore read in progress), overwrite the write in progress data -+ * -+ * This allows the reads and writes to act as if they occur synchronously to the thread scheduling them, however -+ * it fails to properly propagate write failures -+ * -+ */ -+ -+ void reschedule(final int priority) { -+ // priority is checked before this stage // TODO what -+ this.queue.lazySet(null); -+ this.priority.lazySet(priority); -+ PaperFileIOThread.Holder.INSTANCE.queueTask(this); -+ } -+ -+ @Override -+ public void run() { -+ if (true) throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage -+ ChunkDataController.InProgressRead read = this.inProgressRead; -+ if (read != null) { -+ CompoundTag compound = PaperFileIOThread.FAILURE_VALUE; -+ try { -+ compound = this.taskController.readData(this.x, this.z); -+ } catch (final Throwable thr) { -+ if (thr instanceof ThreadDeath) { -+ throw (ThreadDeath)thr; -+ } -+ LOGGER.error("Failed to read chunk data for task: " + this.toString(), thr); -+ // fall through to complete with null data -+ } -+ read.readFuture.complete(compound); -+ } -+ -+ final Long chunkKey = Long.valueOf(IOUtil.getCoordinateKey(this.x, this.z)); -+ -+ ChunkDataController.InProgressWrite write = this.inProgressWrite; -+ -+ if (write == null) { -+ // IntelliJ warns this is invalid, however it does not consider that writes to the task map & the inProgress field can occur concurrently. -+ ChunkDataTask inMap = this.taskController.tasks.compute(chunkKey, (final Long keyInMap, final ChunkDataTask valueInMap) -> { -+ if (valueInMap == null) { -+ throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkDataTask.this.toString() + ", report this!"); -+ } -+ if (valueInMap != ChunkDataTask.this) { -+ throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkDataTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!"); -+ } -+ return valueInMap.inProgressWrite == null ? null : valueInMap; -+ }); -+ -+ if (inMap == null) { -+ return; // set the task value to null, indicating we're done -+ } -+ -+ // not null, which means there was a concurrent write -+ write = this.inProgressWrite; -+ } -+ -+ for (;;) { -+ final long writeCounter; -+ final CompoundTag data; -+ -+ //noinspection SynchronizationOnLocalVariableOrMethodParameter -+ synchronized (write) { -+ writeCounter = write.writeCounter; -+ data = write.data; -+ } -+ -+ boolean failedWrite = false; -+ -+ try { -+ this.taskController.writeData(this.x, this.z, data); -+ } catch (final Throwable thr) { -+ if (thr instanceof ThreadDeath) { -+ throw (ThreadDeath)thr; -+ } -+ LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr); -+ failedWrite = true; -+ } -+ -+ boolean finalFailWrite = failedWrite; -+ -+ ChunkDataTask inMap = this.taskController.tasks.compute(chunkKey, (final Long keyInMap, final ChunkDataTask valueInMap) -> { -+ if (valueInMap == null) { -+ throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkDataTask.this.toString() + ", report this!"); -+ } -+ if (valueInMap != ChunkDataTask.this) { -+ throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkDataTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!"); -+ } -+ if (valueInMap.inProgressWrite.writeCounter == writeCounter) { -+ if (finalFailWrite) { -+ valueInMap.inProgressWrite.writeCounter = -1L; -+ } -+ -+ return null; -+ } -+ return valueInMap; -+ // Hack end -+ }); -+ -+ if (inMap == null) { -+ // write counter matched, so we wrote the most up-to-date pending data, we're done here -+ // or we failed to write and successfully set the write counter to -1 -+ return; // we're done here -+ } -+ -+ // fetch & write new data -+ continue; -+ } -+ } -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/io/PrioritizedTaskQueue.java b/src/main/java/com/destroystokyo/paper/io/PrioritizedTaskQueue.java -new file mode 100644 -index 0000000000000000000000000000000000000000..7844a3515430472bd829ff246396bceb0797de1b ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/PrioritizedTaskQueue.java -@@ -0,0 +1,299 @@ -+package com.destroystokyo.paper.io; -+ -+import java.util.concurrent.ConcurrentLinkedQueue; -+import java.util.concurrent.atomic.AtomicBoolean; -+import java.util.concurrent.atomic.AtomicInteger; -+import java.util.concurrent.atomic.AtomicReference; -+ -+@Deprecated(forRemoval = true) -+public class PrioritizedTaskQueue { -+ -+ // lower numbers are a higher priority (except < 0) -+ // higher priorities are always executed before lower priorities -+ -+ /** -+ * Priority value indicating the task has completed or is being completed. -+ */ -+ public static final int COMPLETING_PRIORITY = -1; -+ -+ /** -+ * Highest priority, should only be used for main thread tasks or tasks that are blocking the main thread. -+ */ -+ public static final int HIGHEST_PRIORITY = 0; -+ -+ /** -+ * Should be only used in an IO task so that chunk loads do not wait on other IO tasks. -+ * This only exists because IO tasks are scheduled before chunk load tasks to decrease IO waiting times. -+ */ -+ public static final int HIGHER_PRIORITY = 1; -+ -+ /** -+ * Should be used for scheduling chunk loads/generation that would increase response times to users. -+ */ -+ public static final int HIGH_PRIORITY = 2; -+ -+ /** -+ * Default priority. -+ */ -+ public static final int NORMAL_PRIORITY = 3; -+ -+ /** -+ * Use for tasks not at all critical and can potentially be delayed. -+ */ -+ public static final int LOW_PRIORITY = 4; -+ -+ /** -+ * Use for tasks that should "eventually" execute. -+ */ -+ public static final int LOWEST_PRIORITY = 5; -+ -+ private static final int TOTAL_PRIORITIES = 6; -+ -+ final ConcurrentLinkedQueue[] queues = (ConcurrentLinkedQueue[])new ConcurrentLinkedQueue[TOTAL_PRIORITIES]; -+ -+ private final AtomicBoolean shutdown = new AtomicBoolean(); -+ -+ { -+ for (int i = 0; i < TOTAL_PRIORITIES; ++i) { -+ this.queues[i] = new ConcurrentLinkedQueue<>(); -+ } -+ } -+ -+ /** -+ * Returns whether the specified priority is valid -+ */ -+ public static boolean validPriority(final int priority) { -+ return priority >= 0 && priority < TOTAL_PRIORITIES; -+ } -+ -+ /** -+ * Queues a task. -+ * @throws IllegalStateException If the task has already been queued. Use {@link PrioritizedTask#raisePriority(int)} to -+ * raise a task's priority. -+ * This can also be thrown if the queue has shutdown. -+ */ -+ public void add(final T task) throws IllegalStateException { -+ int priority = task.getPriority(); -+ if (priority != COMPLETING_PRIORITY) { -+ task.setQueue(this); -+ this.queues[priority].add(task); -+ } -+ if (this.shutdown.get()) { -+ // note: we're not actually sure at this point if our task will go through -+ throw new IllegalStateException("Queue has shutdown, refusing to execute task " + IOUtil.genericToString(task)); -+ } -+ } -+ -+ /** -+ * Polls the highest priority task currently available. {@code null} if none. -+ */ -+ public T poll() { -+ T task; -+ for (int i = 0; i < TOTAL_PRIORITIES; ++i) { -+ final ConcurrentLinkedQueue queue = this.queues[i]; -+ -+ while ((task = queue.poll()) != null) { -+ final int prevPriority = task.tryComplete(i); -+ if (prevPriority != COMPLETING_PRIORITY && prevPriority <= i) { -+ // if the prev priority was greater-than or equal to our current priority -+ return task; -+ } -+ } -+ } -+ -+ return null; -+ } -+ -+ /** -+ * Polls the highest priority task currently available. {@code null} if none. -+ */ -+ public T poll(final int lowestPriority) { -+ T task; -+ final int max = Math.min(LOWEST_PRIORITY, lowestPriority); -+ for (int i = 0; i <= max; ++i) { -+ final ConcurrentLinkedQueue queue = this.queues[i]; -+ -+ while ((task = queue.poll()) != null) { -+ final int prevPriority = task.tryComplete(i); -+ if (prevPriority != COMPLETING_PRIORITY && prevPriority <= i) { -+ // if the prev priority was greater-than or equal to our current priority -+ return task; -+ } -+ } -+ } -+ -+ return null; -+ } -+ -+ /** -+ * Returns whether this queue may have tasks queued. -+ *

    -+ * This operation is not atomic, but is MT-Safe. -+ *

    -+ * @return {@code true} if tasks may be queued, {@code false} otherwise -+ */ -+ public boolean hasTasks() { -+ for (int i = 0; i < TOTAL_PRIORITIES; ++i) { -+ final ConcurrentLinkedQueue queue = this.queues[i]; -+ -+ if (queue.peek() != null) { -+ return true; -+ } -+ } -+ return false; -+ } -+ -+ /** -+ * Prevent further additions to this queue. Attempts to add after this call has completed (potentially during) will -+ * result in {@link IllegalStateException} being thrown. -+ *

    -+ * This operation is atomic with respect to other shutdown calls -+ *

    -+ *

    -+ * After this call has completed, regardless of return value, this queue will be shutdown. -+ *

    -+ * @return {@code true} if the queue was shutdown, {@code false} if it has shut down already -+ */ -+ public boolean shutdown() { -+ return this.shutdown.getAndSet(false); -+ } -+ -+ public abstract static class PrioritizedTask { -+ -+ protected final AtomicReference queue = new AtomicReference<>(); -+ -+ protected final AtomicInteger priority; -+ -+ protected PrioritizedTask() { -+ this(PrioritizedTaskQueue.NORMAL_PRIORITY); -+ } -+ -+ protected PrioritizedTask(final int priority) { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalArgumentException("Invalid priority " + priority); -+ } -+ this.priority = new AtomicInteger(priority); -+ } -+ -+ /** -+ * Returns the current priority. Note that {@link PrioritizedTaskQueue#COMPLETING_PRIORITY} will be returned -+ * if this task is completing or has completed. -+ */ -+ public final int getPriority() { -+ return this.priority.get(); -+ } -+ -+ /** -+ * Returns whether this task is scheduled to execute, or has been already executed. -+ */ -+ public boolean isScheduled() { -+ return this.queue.get() != null; -+ } -+ -+ final int tryComplete(final int minPriority) { -+ for (int curr = this.getPriorityVolatile();;) { -+ if (curr == COMPLETING_PRIORITY) { -+ return COMPLETING_PRIORITY; -+ } -+ if (curr > minPriority) { -+ // curr is lower priority -+ return curr; -+ } -+ -+ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, COMPLETING_PRIORITY))) { -+ return curr; -+ } -+ continue; -+ } -+ } -+ -+ /** -+ * Forces this task to be completed. -+ * @return {@code true} if the task was cancelled, {@code false} if the task has already completed or is being completed. -+ */ -+ public boolean cancel() { -+ return this.exchangePriorityVolatile(PrioritizedTaskQueue.COMPLETING_PRIORITY) != PrioritizedTaskQueue.COMPLETING_PRIORITY; -+ } -+ -+ /** -+ * Attempts to raise the priority to the priority level specified. -+ * @param priority Priority specified -+ * @return {@code true} if successful, {@code false} otherwise. -+ */ -+ public boolean raisePriority(final int priority) { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalArgumentException("Invalid priority"); -+ } -+ -+ for (int curr = this.getPriorityVolatile();;) { -+ if (curr == COMPLETING_PRIORITY) { -+ return false; -+ } -+ if (priority >= curr) { -+ return true; -+ } -+ -+ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority))) { -+ PrioritizedTaskQueue queue = this.queue.get(); -+ if (queue != null) { -+ //noinspection unchecked -+ queue.queues[priority].add(this); // silently fail on shutdown -+ } -+ return true; -+ } -+ continue; -+ } -+ } -+ -+ /** -+ * Attempts to set this task's priority level to the level specified. -+ * @param priority Specified priority level. -+ * @return {@code true} if successful, {@code false} if this task is completing or has completed. -+ */ -+ public boolean updatePriority(final int priority) { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalArgumentException("Invalid priority"); -+ } -+ -+ for (int curr = this.getPriorityVolatile();;) { -+ if (curr == COMPLETING_PRIORITY) { -+ return false; -+ } -+ if (curr == priority) { -+ return true; -+ } -+ -+ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority))) { -+ PrioritizedTaskQueue queue = this.queue.get(); -+ if (queue != null) { -+ //noinspection unchecked -+ queue.queues[priority].add(this); // silently fail on shutdown -+ } -+ return true; -+ } -+ continue; -+ } -+ } -+ -+ void setQueue(final PrioritizedTaskQueue queue) { -+ this.queue.set(queue); -+ } -+ -+ /* priority */ -+ -+ protected final int getPriorityVolatile() { -+ return this.priority.get(); -+ } -+ -+ protected final int compareAndExchangePriorityVolatile(final int expect, final int update) { -+ if (this.priority.compareAndSet(expect, update)) { -+ return expect; -+ } -+ return this.priority.get(); -+ } -+ -+ protected final int exchangePriorityVolatile(final int value) { -+ return this.priority.getAndSet(value); -+ } -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java b/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java -new file mode 100644 -index 0000000000000000000000000000000000000000..99f49b5625cf51d6c97640553cf5c420bb6fdd36 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java -@@ -0,0 +1,255 @@ -+package com.destroystokyo.paper.io; -+ -+import com.mojang.logging.LogUtils; -+import org.slf4j.Logger; -+ -+import java.util.concurrent.ConcurrentLinkedQueue; -+import java.util.concurrent.atomic.AtomicBoolean; -+import java.util.concurrent.locks.LockSupport; -+ -+@Deprecated(forRemoval = true) -+public class QueueExecutorThread extends Thread { -+ -+ private static final Logger LOGGER = LogUtils.getLogger(); -+ -+ protected final PrioritizedTaskQueue queue; -+ protected final long spinWaitTime; -+ -+ protected volatile boolean closed; -+ -+ protected final AtomicBoolean parked = new AtomicBoolean(); -+ -+ protected volatile ConcurrentLinkedQueue flushQueue = new ConcurrentLinkedQueue<>(); -+ protected volatile long flushCycles; -+ -+ protected int lowestPriorityToPoll = PrioritizedTaskQueue.LOWEST_PRIORITY; -+ -+ public int getLowestPriorityToPoll() { -+ return this.lowestPriorityToPoll; -+ } -+ -+ public void setLowestPriorityToPoll(final int lowestPriorityToPoll) { -+ if (this.isAlive()) { -+ throw new IllegalStateException("Cannot set after starting"); -+ } -+ this.lowestPriorityToPoll = lowestPriorityToPoll; -+ } -+ -+ public QueueExecutorThread(final PrioritizedTaskQueue queue) { -+ this(queue, (int)(1.e6)); // 1.0ms -+ } -+ -+ public QueueExecutorThread(final PrioritizedTaskQueue queue, final long spinWaitTime) { // in ms -+ this.queue = queue; -+ this.spinWaitTime = spinWaitTime; -+ } -+ -+ @Override -+ public void run() { -+ final long spinWaitTime = this.spinWaitTime; -+ main_loop: -+ for (;;) { -+ this.pollTasks(true); -+ -+ // spinwait -+ -+ final long start = System.nanoTime(); -+ -+ for (;;) { -+ // If we are interrpted for any reason, park() will always return immediately. Clear so that we don't needlessly use cpu in such an event. -+ Thread.interrupted(); -+ LockSupport.parkNanos("Spinwaiting on tasks", 1000L); // 1us -+ -+ if (this.pollTasks(true)) { -+ // restart loop, found tasks -+ continue main_loop; -+ } -+ -+ if (this.handleClose()) { -+ return; // we're done -+ } -+ -+ if ((System.nanoTime() - start) >= spinWaitTime) { -+ break; -+ } -+ } -+ -+ if (this.handleClose()) { -+ return; -+ } -+ -+ this.parked.set(true); -+ -+ // We need to parse here to avoid a race condition where a thread queues a task before we set parked to true -+ // (i.e it will not notify us) -+ if (this.pollTasks(true)) { -+ this.parked.set(false); -+ continue; -+ } -+ -+ if (this.handleClose()) { -+ return; -+ } -+ -+ // we don't need to check parked before sleeping, but we do need to check parked in a do-while loop -+ // LockSupport.park() can fail for any reason -+ do { -+ Thread.interrupted(); -+ LockSupport.park("Waiting on tasks"); -+ } while (this.parked.get()); -+ } -+ } -+ -+ protected boolean handleClose() { -+ if (this.closed) { -+ this.pollTasks(true); // this ensures we've emptied the queue -+ this.handleFlushThreads(true); -+ return true; -+ } -+ return false; -+ } -+ -+ protected boolean pollTasks(boolean flushTasks) { -+ Runnable task; -+ boolean ret = false; -+ -+ while ((task = this.queue.poll(this.lowestPriorityToPoll)) != null) { -+ ret = true; -+ try { -+ task.run(); -+ } catch (final Throwable throwable) { -+ if (throwable instanceof ThreadDeath) { -+ throw (ThreadDeath)throwable; -+ } -+ LOGGER.error("Exception thrown from prioritized runnable task in thread '" + this.getName() + "': " + IOUtil.genericToString(task), throwable); -+ } -+ } -+ -+ if (flushTasks) { -+ this.handleFlushThreads(false); -+ } -+ -+ return ret; -+ } -+ -+ protected void handleFlushThreads(final boolean shutdown) { -+ Thread parking; -+ ConcurrentLinkedQueue flushQueue = this.flushQueue; -+ do { -+ ++flushCycles; // may be plain read opaque write -+ while ((parking = flushQueue.poll()) != null) { -+ LockSupport.unpark(parking); -+ } -+ } while (this.pollTasks(false)); -+ -+ if (shutdown) { -+ this.flushQueue = null; -+ -+ // defend against a race condition where a flush thread double-checks right before we set to null -+ while ((parking = flushQueue.poll()) != null) { -+ LockSupport.unpark(parking); -+ } -+ } -+ } -+ -+ /** -+ * Notify's this thread that a task has been added to its queue -+ * @return {@code true} if this thread was waiting for tasks, {@code false} if it is executing tasks -+ */ -+ public boolean notifyTasks() { -+ if (this.parked.get() && this.parked.getAndSet(false)) { -+ LockSupport.unpark(this); -+ return true; -+ } -+ return false; -+ } -+ -+ protected void queueTask(final T task) { -+ this.queue.add(task); -+ this.notifyTasks(); -+ } -+ -+ /** -+ * Waits until this thread's queue is empty. -+ * -+ * @throws IllegalStateException If the current thread is {@code this} thread. -+ */ -+ public void flush() { -+ final Thread currentThread = Thread.currentThread(); -+ -+ if (currentThread == this) { -+ // avoid deadlock -+ throw new IllegalStateException("Cannot flush the queue executor thread while on the queue executor thread"); -+ } -+ -+ // order is important -+ -+ int successes = 0; -+ long lastCycle = -1L; -+ -+ do { -+ final ConcurrentLinkedQueue flushQueue = this.flushQueue; -+ if (flushQueue == null) { -+ return; -+ } -+ -+ flushQueue.add(currentThread); -+ -+ // double check flush queue -+ if (this.flushQueue == null) { -+ return; -+ } -+ -+ final long currentCycle = this.flushCycles; // may be opaque read -+ -+ if (currentCycle == lastCycle) { -+ Thread.yield(); -+ continue; -+ } -+ -+ // force response -+ this.parked.set(false); -+ LockSupport.unpark(this); -+ -+ LockSupport.park("flushing queue executor thread"); -+ -+ // returns whether there are tasks queued, does not return whether there are tasks executing -+ // this is why we cycle twice twice through flush (we know a pollTask call is made after a flush cycle) -+ // we really only need to guarantee that the tasks this thread has queued has gone through, and can leave -+ // tasks queued concurrently that are unsychronized with this thread as undefined behavior -+ if (this.queue.hasTasks()) { -+ successes = 0; -+ } else { -+ ++successes; -+ } -+ -+ } while (successes != 2); -+ -+ } -+ -+ /** -+ * Closes this queue executor's queue and optionally waits for it to empty. -+ *

    -+ * If wait is {@code true}, then the queue will be empty by the time this call completes. -+ *

    -+ *

    -+ * This function is MT-Safe. -+ *

    -+ * @param wait If this call is to wait until the queue is empty -+ * @param killQueue Whether to shutdown this thread's queue -+ * @return whether this thread shut down the queue -+ */ -+ public boolean close(final boolean wait, final boolean killQueue) { -+ boolean ret = !killQueue ? false : this.queue.shutdown(); -+ this.closed = true; -+ -+ // force thread to respond to the shutdown -+ this.parked.set(false); -+ LockSupport.unpark(this); -+ -+ if (wait) { -+ this.flush(); -+ } -+ return ret; -+ } -+} diff --git a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java index 05bddc0697faa8d9d9955d89d76930c84ef7df0d..cbeaadaecf816070b3a37938c8e683180939afc4 100644 --- a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java @@ -4466,10 +3351,10 @@ index 0000000000000000000000000000000000000000..15ee41452992714108efe53b708b5a4e +} diff --git a/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java b/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java new file mode 100644 -index 0000000000000000000000000000000000000000..8a11e10b01fa012b2f98b1c193c53251e848f909 +index 0000000000000000000000000000000000000000..2934f0cf0ef09c84739312b00186c2ef0019a165 --- /dev/null +++ b/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java -@@ -0,0 +1,1338 @@ +@@ -0,0 +1,1343 @@ +package io.papermc.paper.chunk.system.io; + +import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue; @@ -4502,15 +3387,16 @@ index 0000000000000000000000000000000000000000..8a11e10b01fa012b2f98b1c193c53251 +/** + * Prioritised RegionFile I/O executor, responsible for all RegionFile access. + *

    -+ * All functions provided are MT-Safe, however certain ordering constraints are recommended: ++ * All functions provided are MT-Safe, however certain ordering constraints are recommended: ++ *

      + *
    • + * Chunk saves may not occur for unloaded chunks. + *
    • + *
    • + * Tasks must be scheduled on the chunk scheduler thread. + *
    • -+ * By following these constraints, no chunk data loss should occur with the exception of underlying I/O problems. -+ *

      ++ *
    ++ * By following these constraints, no chunk data loss should occur with the exception of underlying I/O problems. + */ +public final class RegionFileIOThread extends PrioritisedQueueExecutorThread { + @@ -4529,16 +3415,12 @@ index 0000000000000000000000000000000000000000..8a11e10b01fa012b2f98b1c193c53251 + protected static final RegionFileType[] CACHED_REGIONFILE_TYPES = RegionFileType.values(); + + private ChunkDataController getControllerFor(final ServerLevel world, final RegionFileType type) { -+ switch (type) { -+ case CHUNK_DATA: -+ return world.chunkDataControllerNew; -+ case POI_DATA: -+ return world.poiDataControllerNew; -+ case ENTITY_DATA: -+ return world.entityDataControllerNew; -+ default: -+ throw new IllegalStateException("Unknown controller type " + type); -+ } ++ return switch (type) { ++ case CHUNK_DATA -> world.chunkDataControllerNew; ++ case POI_DATA -> world.poiDataControllerNew; ++ case ENTITY_DATA -> world.entityDataControllerNew; ++ default -> throw new IllegalStateException("Unknown controller type " + type); ++ }; + } + + /** @@ -4759,7 +3641,7 @@ index 0000000000000000000000000000000000000000..8a11e10b01fa012b2f98b1c193c53251 + } + + /** -+ * Returns the current {@code CompoundTag} pending for write for the specified chunk & regionfile type. ++ * Returns the current {@code CompoundTag} pending for write for the specified chunk and regionfile type. + * Note that this does not copy the result, so do not modify the result returned. + * + * @param world Specified world. @@ -4968,15 +3850,16 @@ index 0000000000000000000000000000000000000000..8a11e10b01fa012b2f98b1c193c53251 + /** + * Schedules the chunk data to be written asynchronously. + *

    -+ * Impl notes: -+ *

    -+ *
  • -+ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means -+ * saves must be scheduled before a chunk is unloaded. -+ *
  • -+ *
  • ++ * Impl notes: ++ *
      ++ *
    • ++ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means ++ * saves must be scheduled before a chunk is unloaded. ++ *
    • ++ *
    • + * Writes may be called concurrently, although only the "later" write will go through. -+ *
    • ++ * ++ *
    + * + * @param world Chunk's world + * @param chunkX Chunk's x coordinate @@ -4994,15 +3877,16 @@ index 0000000000000000000000000000000000000000..8a11e10b01fa012b2f98b1c193c53251 + /** + * Schedules the chunk data to be written asynchronously. + *

    -+ * Impl notes: -+ *

    -+ *
  • -+ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means -+ * saves must be scheduled before a chunk is unloaded. -+ *
  • -+ *
  • -+ * Writes may be called concurrently, although only the "later" write will go through. -+ *
  • ++ * Impl notes: ++ *
      ++ *
    • ++ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means ++ * saves must be scheduled before a chunk is unloaded. ++ *
    • ++ *
    • ++ * Writes may be called concurrently, although only the "later" write will go through. ++ *
    • ++ *
    + * + * @param world Chunk's world + * @param chunkX Chunk's x coordinate @@ -5054,13 +3938,14 @@ index 0000000000000000000000000000000000000000..8a11e10b01fa012b2f98b1c193c53251 + * {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)} + * for single load. + *

    -+ * Impl notes: -+ *

    -+ *
  • -+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may -+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of -+ * data is undefined behaviour, and can cause deadlock. -+ *
  • ++ * Impl notes: ++ *
      ++ *
    • ++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may ++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of ++ * data is undefined behaviour, and can cause deadlock. ++ *
    • ++ *
    + * + * @param world Chunk's world + * @param chunkX Chunk's x coordinate @@ -5086,13 +3971,14 @@ index 0000000000000000000000000000000000000000..8a11e10b01fa012b2f98b1c193c53251 + * {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)} + * for single load. + *

    -+ * Impl notes: -+ *

    -+ *
  • -+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may -+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of -+ * data is undefined behaviour, and can cause deadlock. -+ *
  • ++ * Impl notes: ++ *
      ++ *
    • ++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may ++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of ++ * data is undefined behaviour, and can cause deadlock. ++ *
    • ++ *
    + * + * @param world Chunk's world + * @param chunkX Chunk's x coordinate @@ -5120,13 +4006,14 @@ index 0000000000000000000000000000000000000000..8a11e10b01fa012b2f98b1c193c53251 + * then call {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)} + * for single load. + *

    -+ * Impl notes: -+ *

    -+ *
  • -+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may -+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of -+ * data is undefined behaviour, and can cause deadlock. -+ *
  • ++ * Impl notes: ++ *
      ++ *
    • ++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may ++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of ++ * data is undefined behaviour, and can cause deadlock. ++ *
    • ++ *
    + * + * @param world Chunk's world + * @param chunkX Chunk's x coordinate @@ -5154,13 +4041,14 @@ index 0000000000000000000000000000000000000000..8a11e10b01fa012b2f98b1c193c53251 + * then call {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)} + * for single load. + *

    -+ * Impl notes: -+ *

    -+ *
  • -+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may -+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of -+ * data is undefined behaviour, and can cause deadlock. -+ *
  • ++ * Impl notes: ++ *
      ++ *
    • ++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may ++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of ++ * data is undefined behaviour, and can cause deadlock. ++ *
    • ++ *
    + * + * @param world Chunk's world + * @param chunkX Chunk's x coordinate @@ -5217,13 +4105,14 @@ index 0000000000000000000000000000000000000000..8a11e10b01fa012b2f98b1c193c53251 + * Schedules a load to be executed asynchronously. This task will load the specified regionfile type, and then call + * {@code onComplete}. + *

    -+ * Impl notes: -+ *

    -+ *
  • -+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may -+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of -+ * data is undefined behaviour, and can cause deadlock. -+ *
  • ++ * Impl notes: ++ *
      ++ *
    • ++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may ++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of ++ * data is undefined behaviour, and can cause deadlock. ++ *
    • ++ *
    + * + * @param world Chunk's world + * @param chunkX Chunk's x coordinate @@ -5249,13 +4138,14 @@ index 0000000000000000000000000000000000000000..8a11e10b01fa012b2f98b1c193c53251 + * Schedules a load to be executed asynchronously. This task will load the specified regionfile type, and then call + * {@code onComplete}. + *

    -+ * Impl notes: -+ *

    -+ *
  • -+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may -+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of -+ * data is undefined behaviour, and can cause deadlock. -+ *
  • ++ * Impl notes: ++ *
      ++ *
    • ++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may ++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of ++ * data is undefined behaviour, and can cause deadlock. ++ *
    • ++ *
    + * + * @param world Chunk's world + * @param chunkX Chunk's x coordinate @@ -15720,7 +14610,7 @@ index cea9c098ade00ee87b8efc8164ab72f5279758f0..197224e31175252d8438a8df585bbb65 + } } diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java -index ff8a983e23bcf73ec3f7dd98c48640183647ad4d..200ed770b57e1a9240abf0473968d4b85cbefe3c 100644 +index 8235d23dc9778b9444288d58ffdb5d515df56bf9..1ccf71be865af4d3c5ab8f4323a228a947e0bf3f 100644 --- a/src/main/java/io/papermc/paper/util/MCUtil.java +++ b/src/main/java/io/papermc/paper/util/MCUtil.java @@ -6,17 +6,30 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -15766,7 +14656,7 @@ index ff8a983e23bcf73ec3f7dd98c48640183647ad4d..200ed770b57e1a9240abf0473968d4b8 import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.LinkedBlockingQueue; -@@ -534,6 +550,100 @@ public final class MCUtil { +@@ -533,6 +549,100 @@ public final class MCUtil { } } @@ -16642,7 +15532,7 @@ index deb2d8c22a1c5724d0ac8571f4ea54711988dc4b..72d013d06705b08ed696e3d3b6d631d6 DedicatedServer dedicatedserver1 = new DedicatedServer(optionset, worldLoader.get(), thread, convertable_conversionsession, resourcepackrepository, worldstem, dedicatedserversettings, DataFixers.getDataFixer(), services, LoggerChunkProgressListener::new); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 7d637094afecc2a838f9cc5cc837f8bf63cfd5aa..b27cdfd527d9234b51eba7bb642968fa886d460e 100644 +index 7d637094afecc2a838f9cc5cc837f8bf63cfd5aa..365b11eb92e76cda975a5989a556abcf4c8fdaa4 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -311,7 +311,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { @@ -16790,7 +15679,7 @@ index 7d637094afecc2a838f9cc5cc837f8bf63cfd5aa..b27cdfd527d9234b51eba7bb642968fa if (entity.isRemoved()) { continue; } -@@ -2520,7 +2514,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - BlockPhysicsEvent diff --git a/patches/server/1037-Lag-compensation-ticks.patch b/patches/server/1037-Lag-compensation-ticks.patch index 914d0dda7c..35ac17c281 100644 --- a/patches/server/1037-Lag-compensation-ticks.patch +++ b/patches/server/1037-Lag-compensation-ticks.patch @@ -8,7 +8,7 @@ Areas affected by lag comepnsation: - Eating food items diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 5c26ccc4d58254afce7ece906600a86364e01af8..fa2b4cdd4c4e2c1df3e378a7c706f06c3361ad53 100644 +index a7fb5d121a9fd59d5a3951075e975533721cb87f..90ae7e8c81f6a9ede8aeaeec3bf784023522cefc 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -311,6 +311,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function serverFactory) { AtomicReference atomicreference = new AtomicReference(); -@@ -1689,6 +1690,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop