From 614a664bd3654ee6fb2130873614beda8eff8d62 Mon Sep 17 00:00:00 2001 From: Aikar Date: Tue, 19 May 2020 04:01:53 -0400 Subject: [PATCH] Implement Chunk Priority / Urgency System for Chunks Mark chunks that are blocking main thread for world generation as urgent Implements a general priority system so that chunks that are sorted in the generator queues can prioritize certain chunks over another. Urgent chunks will jump to the front of the line, ensuring that a sync chunk load on an ungenerated chunk does not lag the server for a long period of time if the servers generator queues are filled with lots of chunks already. This massively reduces the lag spikes from sync chunk gens. Then we further prioritize loading order so nearby chunks have higher priority than distant chunks, reducing the pressure a high no tick view distance holds on you. Chunks in front of the player have higher priority, to help with fast traveling players keep up with their movement. This commit also improves single core cpu scenarios in that we will now automatically disable Async Chunks as well as Minecrafts thread pool. It is never recommended to use async chunks on a single CPU as context switching will be slower than just running it all on main. This also bumps the number of server worker threads by default too. Mojang does not utilize the workers in an effecient manner, resulting in them using barely any sustained CPU. So give it more workers so more chunks can be processed concurrently This change also improves urgent chunk loading, so players flying into unloaded chunks will hurt a little bit less (but still hurt) Ping #3395 #3363 (Not marking as closed, we need to make prevent moving work) --- Spigot-Server-Patches/0004-MC-Utils.patch | 49 +- .../0133-String-based-Action-Bar-API.patch | 4 +- .../0154-Basic-PlayerProfile-API.patch | 4 +- ...arseException-in-Entity-and-TE-names.patch | 4 +- ...ble-thread-count-default-for-bootstr.patch | 7 +- .../0377-Chunk-debug-command.patch | 4 +- ...90-Asynchronous-chunk-IO-and-loading.patch | 27 +- ...hunkMap-memory-use-for-visibleChunks.patch | 4 +- ...asks-Speed-up-processing-of-chunk-lo.patch | 12 +- ...dd-Raw-Byte-ItemStack-Serialization.patch} | 0 ...Priority-Urgency-System-for-World-Ge.patch | 312 ----------- ...ions-until-after-entity-ticking-is-d.patch | 2 +- .../0467-Improved-Watchdog-Support.patch | 8 +- ...oviderServer-s-chunk-level-checking-.patch | 4 +- ...-Chunk-Post-Processing-deadlock-risk.patch | 8 +- ...layer-is-attempted-to-be-removed-fro.patch | 2 +- ...83-Broadcast-join-message-to-console.patch | 2 +- ...g-Broken-behavior-of-PlayerJoinEvent.patch | 8 +- ...Load-Chunks-for-Login-Asynchronously.patch | 6 +- ...0487-Allow-sleeping-players-to-float.patch | 2 +- ...allbacks-to-schedule-for-Callback-Ex.patch | 2 +- .../0493-Implement-Brigadier-Mojang-API.patch | 2 +- ...PickItem-Packet-and-kick-for-invalid.patch | 2 +- ...tance-map-to-optimise-entity-tracker.patch | 18 +- ...-isOutsideRange-to-use-distance-maps.patch | 24 +- ...No-Tick-view-distance-implementation.patch | 48 +- .../0507-Fix-Light-Command.patch | 6 +- ...sure-Entity-AABB-s-are-never-invalid.patch | 2 +- ...Wait-for-Async-Tasks-during-shutdown.patch | 2 +- ...WorldBorder-collision-checks-and-air.patch | 2 +- ...and-End-Portal-Frames-from-being-des.patch | 4 +- ...bleInt-allocations-from-light-engine.patch | 4 +- ...llocation-of-Vec3D-by-entity-tracker.patch | 4 +- ...n-for-console-having-all-permissions.patch | 4 +- ...ound-for-Client-Lag-Spikes-MC-162253.patch | 4 +- ...k-Priority-Urgency-System-for-Chunks.patch | 524 ++++++++++++++++++ 36 files changed, 697 insertions(+), 424 deletions(-) rename Spigot-Server-Patches/{0529-Add-Raw-Byte-ItemStack-Serialization.patch => 0463-Add-Raw-Byte-ItemStack-Serialization.patch} (100%) delete mode 100644 Spigot-Server-Patches/0463-Implement-Chunk-Priority-Urgency-System-for-World-Ge.patch create mode 100644 Spigot-Server-Patches/0529-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch diff --git a/Spigot-Server-Patches/0004-MC-Utils.patch b/Spigot-Server-Patches/0004-MC-Utils.patch index 6a4038d40..e34a6ed15 100644 --- a/Spigot-Server-Patches/0004-MC-Utils.patch +++ b/Spigot-Server-Patches/0004-MC-Utils.patch @@ -1095,10 +1095,10 @@ index 0000000000000000000000000000000000000000..c3b936f54b3fff418c265639ef223292 +} diff --git a/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java new file mode 100644 -index 0000000000000000000000000000000000000000..c71ed11834557f71504de5038d3bb593824f6f95 +index 0000000000000000000000000000000000000000..1330df2c1d3c4f52dad0adeb169409eb412814ab --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java -@@ -0,0 +1,439 @@ +@@ -0,0 +1,453 @@ +package com.destroystokyo.paper.util.misc; + +import com.destroystokyo.paper.util.math.IntegerUtil; @@ -1132,6 +1132,7 @@ index 0000000000000000000000000000000000000000..c71ed11834557f71504de5038d3bb593 + + protected final ChangeCallback addCallback; + protected final ChangeCallback removeCallback; ++ protected final ChangeSourceCallback changeSourceCallback; + + public AreaMap() { + this(new PooledLinkedHashSets<>()); @@ -1143,9 +1144,13 @@ index 0000000000000000000000000000000000000000..c71ed11834557f71504de5038d3bb593 + } + + public AreaMap(final PooledLinkedHashSets pooledHashSets, final ChangeCallback addCallback, final ChangeCallback removeCallback) { ++ this(pooledHashSets, addCallback, removeCallback, null); ++ } ++ public AreaMap(final PooledLinkedHashSets pooledHashSets, final ChangeCallback addCallback, final ChangeCallback removeCallback, final ChangeSourceCallback changeSourceCallback) { + this.pooledHashSets = pooledHashSets; + this.addCallback = addCallback; + this.removeCallback = removeCallback; ++ this.changeSourceCallback = changeSourceCallback; + } + + @Nullable @@ -1208,7 +1213,11 @@ index 0000000000000000000000000000000000000000..c71ed11834557f71504de5038d3bb593 + } + + // called after the distance map updates -+ protected void updateObjectCallback(final E Object, final long oldPosition, final long newPosition, final int oldViewDistance, final int newViewDistance) {} ++ protected void updateObjectCallback(final E Object, final long oldPosition, final long newPosition, final int oldViewDistance, final int newViewDistance) { ++ if (newPosition != oldPosition && this.changeSourceCallback != null) { ++ this.changeSourceCallback.accept(Object, oldPosition, newPosition); ++ } ++ } + + public final boolean add(final E object, final int chunkX, final int chunkZ, final int viewDistance) { + final int oldViewDistance = this.objectToViewDistance.putIfAbsent(object, viewDistance); @@ -1537,6 +1546,11 @@ index 0000000000000000000000000000000000000000..c71ed11834557f71504de5038d3bb593 + final PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState); + + } ++ ++ @FunctionalInterface ++ public static interface ChangeSourceCallback { ++ void accept(final E object, final long prevPos, final long newPos); ++ } +} diff --git a/src/main/java/com/destroystokyo/paper/util/misc/DistanceTrackingAreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/DistanceTrackingAreaMap.java new file mode 100644 @@ -1721,10 +1735,10 @@ index 0000000000000000000000000000000000000000..3f86c1ad43782bdc56be6c0eca053311 +} diff --git a/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java new file mode 100644 -index 0000000000000000000000000000000000000000..8a552a87abf5cc6fc0e10bf93de3cf8168d57cb5 +index 0000000000000000000000000000000000000000..b1396f405d041fc3ca1f7ce1e0f884a3cfb8b96e --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java -@@ -0,0 +1,27 @@ +@@ -0,0 +1,32 @@ +package com.destroystokyo.paper.util.misc; + +import net.minecraft.server.EntityPlayer; @@ -1744,7 +1758,12 @@ index 0000000000000000000000000000000000000000..8a552a87abf5cc6fc0e10bf93de3cf81 + + public PlayerAreaMap(final PooledLinkedHashSets pooledHashSets, final ChangeCallback addCallback, + final ChangeCallback removeCallback) { -+ super(pooledHashSets, addCallback, removeCallback); ++ this(pooledHashSets, addCallback, removeCallback, null); ++ } ++ ++ public PlayerAreaMap(final PooledLinkedHashSets pooledHashSets, final ChangeCallback addCallback, ++ final ChangeCallback removeCallback, final ChangeSourceCallback changeSourceCallback) { ++ super(pooledHashSets, addCallback, removeCallback, changeSourceCallback); + } + + @Override @@ -3335,10 +3354,10 @@ index 75308712d0642d5ab168de653023349df8aee5ed..aa7501d366b15e7f7f64b7d98a1dccff // CraftBukkit end diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java new file mode 100644 -index 0000000000000000000000000000000000000000..7164f46516bdf49ed52062f2d72f33418506bae0 +index 0000000000000000000000000000000000000000..b40cd1fad5c9e2f0f85c87a559caf2b780814017 --- /dev/null +++ b/src/main/java/net/minecraft/server/MCUtil.java -@@ -0,0 +1,473 @@ +@@ -0,0 +1,487 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.block.TargetBlockInfo; @@ -3467,6 +3486,20 @@ index 0000000000000000000000000000000000000000..7164f46516bdf49ed52062f2d72f3341 + return x < (double)truncated ? truncated - 1 : truncated; + } + ++ public static float normalizeYaw(float f) { ++ float f1 = f % 360.0F; ++ ++ if (f1 >= 180.0F) { ++ f1 -= 360.0F; ++ } ++ ++ if (f1 < -180.0F) { ++ f1 += 360.0F; ++ } ++ ++ return f1; ++ } ++ + public static int fastFloor(float x) { + int truncated = (int)x; + return x < (double)truncated ? truncated - 1 : truncated; diff --git a/Spigot-Server-Patches/0133-String-based-Action-Bar-API.patch b/Spigot-Server-Patches/0133-String-based-Action-Bar-API.patch index c7c39d887..2cbb8d05d 100644 --- a/Spigot-Server-Patches/0133-String-based-Action-Bar-API.patch +++ b/Spigot-Server-Patches/0133-String-based-Action-Bar-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] String based Action Bar API diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java -index 7164f46516bdf49ed52062f2d72f33418506bae0..87bd51ab1b844e05ecd3d9d2555841554976023c 100644 +index b40cd1fad5c9e2f0f85c87a559caf2b780814017..f9a7a1e9eea67bafb85c0ed88e96abb8e45f6c81 100644 --- a/src/main/java/net/minecraft/server/MCUtil.java +++ b/src/main/java/net/minecraft/server/MCUtil.java @@ -2,6 +2,7 @@ package net.minecraft.server; @@ -16,7 +16,7 @@ index 7164f46516bdf49ed52062f2d72f33418506bae0..87bd51ab1b844e05ecd3d9d255584155 import org.bukkit.Location; import org.bukkit.block.BlockFace; import org.bukkit.craftbukkit.CraftWorld; -@@ -181,6 +182,24 @@ public final class MCUtil { +@@ -195,6 +196,24 @@ public final class MCUtil { private MCUtil() {} diff --git a/Spigot-Server-Patches/0154-Basic-PlayerProfile-API.patch b/Spigot-Server-Patches/0154-Basic-PlayerProfile-API.patch index 04681c9f3..747c485c4 100644 --- a/Spigot-Server-Patches/0154-Basic-PlayerProfile-API.patch +++ b/Spigot-Server-Patches/0154-Basic-PlayerProfile-API.patch @@ -403,7 +403,7 @@ index 0000000000000000000000000000000000000000..3aceb0ea8a1a3ed94dd8a9e954c52ecd + } +} diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java -index 87bd51ab1b844e05ecd3d9d2555841554976023c..37b8257552efd0d66a020f0b5007514f2ac890cd 100644 +index f9a7a1e9eea67bafb85c0ed88e96abb8e45f6c81..8ca1a4719d934db31d57b34cf7050acc5a1a7048 100644 --- a/src/main/java/net/minecraft/server/MCUtil.java +++ b/src/main/java/net/minecraft/server/MCUtil.java @@ -1,7 +1,10 @@ @@ -417,7 +417,7 @@ index 87bd51ab1b844e05ecd3d9d2555841554976023c..37b8257552efd0d66a020f0b5007514f import org.apache.commons.lang.exception.ExceptionUtils; import org.bukkit.Location; import org.bukkit.block.BlockFace; -@@ -315,6 +318,10 @@ public final class MCUtil { +@@ -329,6 +332,10 @@ public final class MCUtil { return run.get(); } diff --git a/Spigot-Server-Patches/0301-Catch-JsonParseException-in-Entity-and-TE-names.patch b/Spigot-Server-Patches/0301-Catch-JsonParseException-in-Entity-and-TE-names.patch index eff839dfe..a598dc8d7 100644 --- a/Spigot-Server-Patches/0301-Catch-JsonParseException-in-Entity-and-TE-names.patch +++ b/Spigot-Server-Patches/0301-Catch-JsonParseException-in-Entity-and-TE-names.patch @@ -39,10 +39,10 @@ index 0f74ec89b3e85c918c95f9d8fef6d68403ed1107..4609e402b419ed21e17ad34d02dca55b this.setCustomNameVisible(nbttagcompound.getBoolean("CustomNameVisible")); diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java -index 37b8257552efd0d66a020f0b5007514f2ac890cd..be20d770df41a656cf2aabfec87e0bdc639053f4 100644 +index 8ca1a4719d934db31d57b34cf7050acc5a1a7048..206d04dcce1d7d074cf7151a083bdc626b0b8e07 100644 --- a/src/main/java/net/minecraft/server/MCUtil.java +++ b/src/main/java/net/minecraft/server/MCUtil.java -@@ -496,4 +496,19 @@ public final class MCUtil { +@@ -510,4 +510,19 @@ public final class MCUtil { return null; } } diff --git a/Spigot-Server-Patches/0320-Use-more-reasonable-thread-count-default-for-bootstr.patch b/Spigot-Server-Patches/0320-Use-more-reasonable-thread-count-default-for-bootstr.patch index 4fb0cc9c9..06ae5f51f 100644 --- a/Spigot-Server-Patches/0320-Use-more-reasonable-thread-count-default-for-bootstr.patch +++ b/Spigot-Server-Patches/0320-Use-more-reasonable-thread-count-default-for-bootstr.patch @@ -5,15 +5,16 @@ Subject: [PATCH] Use more reasonable thread count default for bootstrap diff --git a/src/main/java/net/minecraft/server/SystemUtils.java b/src/main/java/net/minecraft/server/SystemUtils.java -index 7e224ebeff3bf34270df173a47b08d3290c00670..dc6d030621b66e43edf3a148f0eca43382383705 100644 +index 7e224ebeff3bf34270df173a47b08d3290c00670..60d7c06d4c5bd054a411c915af3ae685df24ccd4 100644 --- a/src/main/java/net/minecraft/server/SystemUtils.java +++ b/src/main/java/net/minecraft/server/SystemUtils.java -@@ -66,7 +66,7 @@ public class SystemUtils { +@@ -66,7 +66,8 @@ public class SystemUtils { } private static ExecutorService k() { - int i = MathHelper.clamp(Runtime.getRuntime().availableProcessors() - 1, 1, 7); -+ int i = Math.min(6, Math.max(Runtime.getRuntime().availableProcessors() - 2, 2)); // Paper - use more reasonable default - 2 is hard minimum to avoid using unlimited threads ++ int i = Math.min(8, Math.max(Runtime.getRuntime().availableProcessors() - 2, 3)); // Paper - use more reasonable default - 2 is hard minimum to avoid using unlimited threads ++ i = Integer.getInteger("Paper.WorkerThreadCount", i); // Paper - allow overriding Object object; if (i <= 0) { diff --git a/Spigot-Server-Patches/0377-Chunk-debug-command.patch b/Spigot-Server-Patches/0377-Chunk-debug-command.patch index 19798635b..0b47d2e3f 100644 --- a/Spigot-Server-Patches/0377-Chunk-debug-command.patch +++ b/Spigot-Server-Patches/0377-Chunk-debug-command.patch @@ -198,7 +198,7 @@ index 8c6550433c20c54cbe390219821ce393c5720da8..e6d08756f76360b29b29f18305e5ec84 public final ChunkGenerator chunkGenerator; private final WorldServer world; diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java -index be20d770df41a656cf2aabfec87e0bdc639053f4..de8b8f54cd906c1154a6790b9220d3e0976c74bd 100644 +index 206d04dcce1d7d074cf7151a083bdc626b0b8e07..f75f48a3d0b0bc1da3c5ae3b3cf20b64f1e8288a 100644 --- a/src/main/java/net/minecraft/server/MCUtil.java +++ b/src/main/java/net/minecraft/server/MCUtil.java @@ -4,7 +4,13 @@ import com.destroystokyo.paper.block.TargetBlockInfo; @@ -227,7 +227,7 @@ index be20d770df41a656cf2aabfec87e0bdc639053f4..de8b8f54cd906c1154a6790b9220d3e0 import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.LinkedBlockingQueue; -@@ -511,4 +520,170 @@ public final class MCUtil { +@@ -525,4 +534,170 @@ public final class MCUtil { return null; } diff --git a/Spigot-Server-Patches/0390-Asynchronous-chunk-IO-and-loading.patch b/Spigot-Server-Patches/0390-Asynchronous-chunk-IO-and-loading.patch index 5665ff357..585330c1a 100644 --- a/Spigot-Server-Patches/0390-Asynchronous-chunk-IO-and-loading.patch +++ b/Spigot-Server-Patches/0390-Asynchronous-chunk-IO-and-loading.patch @@ -161,7 +161,7 @@ index fa1c920ea6092259149f9e7f9cd7cc1ed27bf338..98acbfa44dd9042b26fdf719d7748f92 public static Timing getTickList(WorldServer worldserver, String timingsType) { diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index dbd14399707cdd43f98af40191be8ff3e76edf43..f4836e2da1061e7aa62ddb01c8ca7b3467b18415 100644 +index dbd14399707cdd43f98af40191be8ff3e76edf43..74295466e53db06d0d019a13768f3575ac61d699 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -1,5 +1,6 @@ @@ -171,7 +171,7 @@ index dbd14399707cdd43f98af40191be8ff3e76edf43..f4836e2da1061e7aa62ddb01c8ca7b34 import com.google.common.base.Strings; import com.google.common.base.Throwables; -@@ -367,4 +368,50 @@ public class PaperConfig { +@@ -367,4 +368,54 @@ public class PaperConfig { maxBookPageSize = getInt("settings.book-size.page-max", maxBookPageSize); maxBookTotalSizeMultiplier = getDouble("settings.book-size.total-multiplier", maxBookTotalSizeMultiplier); } @@ -202,8 +202,12 @@ index dbd14399707cdd43f98af40191be8ff3e76edf43..f4836e2da1061e7aa62ddb01c8ca7b34 + + asyncChunks = getBoolean("settings.async-chunks.enable", true); + int threads = getInt("settings.async-chunks.threads", -1); ++ int cpus = Runtime.getRuntime().availableProcessors(); + if (threads <= 0) { -+ threads = (int) Math.min(Integer.getInteger("paper.maxChunkThreads", 8), Math.max(1, Runtime.getRuntime().availableProcessors() - 1)); ++ threads = (int) Math.min(Integer.getInteger("paper.maxChunkThreads", 8), Math.max(1, cpus - 1)); ++ } ++ if (cpus == 1 && !Boolean.getBoolean("Paper.allowAsyncChunksSingleCore")) { ++ asyncChunks = false; + } + + // Let Shared Host set some limits @@ -2927,10 +2931,10 @@ index 2f95174fcc467908808ed3f2dc956bdcafdc3558..134c76065bf382912e6c28d15449db3f +// Paper end } diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java -index de8b8f54cd906c1154a6790b9220d3e0976c74bd..f8a1f0b96f2eb8535e3080db979bb383d5a18a11 100644 +index f75f48a3d0b0bc1da3c5ae3b3cf20b64f1e8288a..0e01e5c2c008823355e370d0c9ced79130e5fb92 100644 --- a/src/main/java/net/minecraft/server/MCUtil.java +++ b/src/main/java/net/minecraft/server/MCUtil.java -@@ -686,4 +686,9 @@ public final class MCUtil { +@@ -700,4 +700,9 @@ public final class MCUtil { out.print(fileData); } } @@ -3845,6 +3849,19 @@ index db9f0196bda4c987de6cf63eea437b7154d47b57..a6d8ef5eb44f3f851a3a1be4032ca21a } + // Paper end } +diff --git a/src/main/java/net/minecraft/server/SystemUtils.java b/src/main/java/net/minecraft/server/SystemUtils.java +index 60d7c06d4c5bd054a411c915af3ae685df24ccd4..47dbcb70a6b41438a5bf3196495cfdaf02bd2b74 100644 +--- a/src/main/java/net/minecraft/server/SystemUtils.java ++++ b/src/main/java/net/minecraft/server/SystemUtils.java +@@ -70,7 +70,7 @@ public class SystemUtils { + i = Integer.getInteger("Paper.WorkerThreadCount", i); // Paper - allow overriding + Object object; + +- if (i <= 0) { ++ if (i <= 0 || (Runtime.getRuntime().availableProcessors() == 1 && !Boolean.getBoolean("Paper.allowAsyncChunksSingleCore"))) { // Paper - disable server worker queue if single core system + object = MoreExecutors.newDirectExecutorService(); + } else { + object = new ForkJoinPool(i, (forkjoinpool) -> { diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java index 75ab9f185b3231113dfa387c956a707b403bb2db..8055f5998213ab1c6c10d03d88d2b14d220a5e40 100644 --- a/src/main/java/net/minecraft/server/TicketType.java diff --git a/Spigot-Server-Patches/0456-Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch b/Spigot-Server-Patches/0456-Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch index 3b16c1f78..3721bf78d 100644 --- a/Spigot-Server-Patches/0456-Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch +++ b/Spigot-Server-Patches/0456-Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch @@ -63,10 +63,10 @@ index fd998e4fb1534690a2ef8c1bca55e0ae9fe855f9..8f849d83d08b39f1cd9184f484a2089a if (optional.isPresent()) { diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java -index f8a1f0b96f2eb8535e3080db979bb383d5a18a11..88b41b1d0c2045d01449256a5875ae73765c5595 100644 +index 0e01e5c2c008823355e370d0c9ced79130e5fb92..d129c7f54d9f65fff6f512d8ff5f1c3866632603 100644 --- a/src/main/java/net/minecraft/server/MCUtil.java +++ b/src/main/java/net/minecraft/server/MCUtil.java -@@ -588,7 +588,7 @@ public final class MCUtil { +@@ -602,7 +602,7 @@ public final class MCUtil { WorldServer world = ((org.bukkit.craftbukkit.CraftWorld)bukkitWorld).getHandle(); PlayerChunkMap chunkMap = world.getChunkProvider().playerChunkMap; diff --git a/Spigot-Server-Patches/0459-Mid-Tick-Chunk-Tasks-Speed-up-processing-of-chunk-lo.patch b/Spigot-Server-Patches/0459-Mid-Tick-Chunk-Tasks-Speed-up-processing-of-chunk-lo.patch index df6324af8..3acefa77c 100644 --- a/Spigot-Server-Patches/0459-Mid-Tick-Chunk-Tasks-Speed-up-processing-of-chunk-lo.patch +++ b/Spigot-Server-Patches/0459-Mid-Tick-Chunk-Tasks-Speed-up-processing-of-chunk-lo.patch @@ -42,10 +42,10 @@ index 223d3b1125d0781758c45c6b469e6cccd13f187a..37341d2d2e7010b403708b6fc52524e8 public static final Timing commandFunctionsTimer = Timings.ofSafe("Command Functions"); public static final Timing connectionTimer = Timings.ofSafe("Connection Handler"); diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 647f6fc8efb350fbd0bc4c40358a998f8b89b96a..9f1662ece533f5ea744662b718e2d89ace3107fb 100644 +index f1b41e16c8ce8323a896339c5d822f8ff7d8f7e6..f8f225e18fa38cad917f52a379233e0a7a869b07 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -421,4 +421,9 @@ public class PaperConfig { +@@ -425,4 +425,9 @@ public class PaperConfig { log("Async Chunks: Enabled - Chunks will be loaded much faster, without lag."); } } @@ -56,7 +56,7 @@ index 647f6fc8efb350fbd0bc4c40358a998f8b89b96a..9f1662ece533f5ea744662b718e2d89a + } } diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 8f849d83d08b39f1cd9184f484a2089a7a3124ef..5806ca545191e609bab04e522e358948cf32b21c 100644 +index 8f849d83d08b39f1cd9184f484a2089a7a3124ef..377d554553ce81f66207541d963f826867e66592 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -688,6 +688,7 @@ public class ChunkProviderServer extends IChunkProvider { @@ -116,7 +116,7 @@ index 8f849d83d08b39f1cd9184f484a2089a7a3124ef..5806ca545191e609bab04e522e358948 + //noinspection StatementWithEmptyBody + while (pollChunkLoadTasks()) {} + -+ if (System.nanoTime() - lastMidTickChunkTask < 1000000) { ++ if (System.nanoTime() - lastMidTickChunkTask < 200000) { + return; + } + @@ -135,7 +135,7 @@ index 8f849d83d08b39f1cd9184f484a2089a7a3124ef..5806ca545191e609bab04e522e358948 protected boolean executeNext() { // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index f9faa30ef914b1dd2dada9b7d89e80b34d2f1d0d..97cca4495a8dab4434e917a5d94192a28581925c 100644 +index f9faa30ef914b1dd2dada9b7d89e80b34d2f1d0d..f9694d188cc61f123fca4d54bf14fb7466b06a6c 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -910,6 +910,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant -Date: Sat, 11 Apr 2020 03:56:07 -0400 -Subject: [PATCH] Implement Chunk Priority / Urgency System for World Gen - -Mark chunks that are blocking main thread for world generation as urgent - -Implements a general priority system so that chunks that are sorted in -the generator queues can prioritize certain chunks over another. - -Urgent chunks will jump to the front of the line, ensuring that a -sync chunk load on an ungenerated chunk does not lag the server for -a long period of time if the servers generator queues are filled with -lots of chunks already. - -This massively reduces the lag spikes from sync chunk gens. - -diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 5806ca545191e609bab04e522e358948cf32b21c..4cef51b68984f83b8153ee1f017a2c597194df19 100644 ---- a/src/main/java/net/minecraft/server/ChunkProviderServer.java -+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -469,6 +469,10 @@ public class ChunkProviderServer extends IChunkProvider { - - if (!completablefuture.isDone()) { // Paper - // Paper start - async chunk io/loading -+ PlayerChunk playerChunk = this.getChunk(ChunkCoordIntPair.pair(x, z)); -+ if (playerChunk != null) { -+ playerChunk.markChunkUrgent(chunkstatus); -+ } - this.world.asyncChunkTaskManager.raisePriority(x, z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); - com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.world, x, z); - // Paper end -@@ -478,6 +482,10 @@ public class ChunkProviderServer extends IChunkProvider { - com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug - this.world.timings.syncChunkLoad.stopTiming(); // Paper - } // Paper -+ PlayerChunk playerChunk = this.getChunk(ChunkCoordIntPair.pair(x, z)); -+ if (playerChunk != null) { -+ playerChunk.clearChunkUrgent(); -+ } - ichunkaccess = (IChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { - return ichunkaccess1; - }, (playerchunk_failure) -> { -@@ -541,6 +549,11 @@ public class ChunkProviderServer extends IChunkProvider { - } - } - } -+ // Paper start -+ if (playerchunk != null && isUrgent) { -+ playerchunk.markChunkUrgent(chunkstatus); -+ } -+ // Paper end - - return this.a(playerchunk, l) ? PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE : playerchunk.a(chunkstatus, this.playerChunkMap); - } -diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 3d610e41969768da0d2848fa1ae195035ccfd660..a6e7bcf79df9f4ad5b0a779f3eecf85f121bedc8 100644 ---- a/src/main/java/net/minecraft/server/PlayerChunk.java -+++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -43,6 +43,111 @@ public class PlayerChunk { - long lastAutoSaveTime; // Paper - incremental autosave - long inactiveTimeStart; // Paper - incremental autosave - -+ // Paper start - Chunk gen/load priority system -+ volatile int chunkPriority = 0; -+ volatile boolean isUrgent = false; -+ final java.util.List urgentNeighbors = new java.util.ArrayList<>(); -+ volatile PlayerChunk rootUrgentOriginator; -+ volatile PlayerChunk urgentOriginator; -+ public void onNeighborRequest(PlayerChunk neighbor, ChunkStatus status) { -+ if (isUrgent && !neighbor.isUrgent && !java.util.Objects.equals(neighbor, rootUrgentOriginator) && !java.util.Objects.equals(neighbor, urgentOriginator)) { -+ synchronized (this.urgentNeighbors) { -+ if (!neighbor.isUrgent) { -+ neighbor.markChunkUrgent(status, this.rootUrgentOriginator, this); -+ this.urgentNeighbors.add(neighbor); -+ } -+ } -+ } -+ } -+ -+ public void onNeighborsDone() { -+ List urgentNeighbors; -+ synchronized (this.urgentNeighbors) { -+ urgentNeighbors = new java.util.ArrayList<>(this.urgentNeighbors); -+ this.urgentNeighbors.clear(); -+ } -+ for (PlayerChunk urgentNeighbor : urgentNeighbors) { -+ if (urgentNeighbor != null) { -+ urgentNeighbor.clearChunkUrgent(this); -+ } -+ } -+ } -+ -+ public void clearChunkUrgent() { -+ clearChunkUrgent(this); -+ } -+ public void clearChunkUrgent(PlayerChunk requester) { -+ if (this.isUrgent && java.util.Objects.equals(requester, this.urgentOriginator)) { -+ this.isUrgent = false; -+ this.urgentOriginator = null; -+ this.rootUrgentOriginator = null; -+ this.onNeighborsDone(); -+ } -+ } -+ -+ public void markChunkUrgent(ChunkStatus targetStatus) { -+ this.markChunkUrgent(targetStatus, this , this); -+ } -+ public void markChunkUrgent(ChunkStatus targetStatus, PlayerChunk rootUrgentOriginator, PlayerChunk urgentOriginator) { -+ if (!this.isUrgent) { -+ this.rootUrgentOriginator = rootUrgentOriginator; -+ this.urgentOriginator = urgentOriginator; -+ this.isUrgent = true; -+ int x = location.x; -+ int z = location.z; -+ IChunkAccess chunk = getAvailableChunkNow(); -+ final ChunkStatus chunkCurrentStatus = chunk == null ? null : chunk.getChunkStatus(); -+ final ChunkStatus completedStatus = this.getChunkHolderStatus(); -+ final ChunkStatus nextStatus = getNextStatus(completedStatus != null ? completedStatus : ChunkStatus.EMPTY); -+ -+ if (chunkCurrentStatus == null || completedStatus == null) { -+ this.chunkMap.world.asyncChunkTaskManager.raisePriority(x, z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); -+ // next status is empty, empty has no neighbours needing loading -+ return; -+ } -+ -+ if (!targetStatus.isAtLeastStatus(nextStatus)) { -+ // we don't want a status greater-than the one we already have, don't prioritise these loads - they will get in the way -+ return; -+ } -+ -+ // at this point we want a chunk that has a status higher than the one we have already completed -+ -+ // does the next status need neighbours at all? -+ final int requiredNeighbours = nextStatus.getNeighborRadius(); -+ if (requiredNeighbours <= 0) { -+ // no it doesn't, we're done here. we've already prioritised this chunk, no neighbours need prioritising -+ return; -+ } -+ -+ // even though we might want a higher status than targetFinalStatus, we cannot queue neighbours for it - we -+ // instead use the current chunk status in progress (nextCompletedStatus) to ensure we aren't waiting on -+ // unprioritised logic for the next status to complete -+ -+ for (int cx = -requiredNeighbours; cx <= requiredNeighbours; ++cx) { -+ for (int cz = -requiredNeighbours; cz <= requiredNeighbours; ++cz) { -+ if (cx == 0 && cz == 0) { -+ continue; -+ } -+ PlayerChunk neighbor = this.chunkMap.getUpdatingChunk(ChunkCoordIntPair.asLong(x + cz, z + cx)); -+ if (neighbor == null) { -+ continue; -+ } -+ -+ IChunkAccess neighborChunk = neighbor.getAvailableChunkNow(); -+ ChunkStatus neededStatus = this.chunkMap.getNeededStatusByRadius(nextStatus, Math.max(Math.abs(cx), Math.abs(cz))); -+ ChunkStatus neighborCurrentStatus = neighborChunk != null ? neighborChunk.getChunkStatus() : ChunkStatus.EMPTY; -+ if (nextStatus == ChunkStatus.LIGHT || !neighborCurrentStatus.isAtLeastStatus(neededStatus)) { -+ // we don't need to gen neighbours if our current chunk's status has already gone through the gen -+ // light is always an exception, no matter what if we go through light we need its neighbours - the light engine requires them -+ this.onNeighborRequest(neighbor, neededStatus); -+ } -+ } -+ } -+ } -+ } -+ // Paper end -+ - public PlayerChunk(ChunkCoordIntPair chunkcoordintpair, int i, LightEngine lightengine, PlayerChunk.c playerchunk_c, PlayerChunk.d playerchunk_d) { - this.statusFutures = new AtomicReferenceArray(PlayerChunk.CHUNK_STATUSES.size()); - this.fullChunkFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE; -@@ -139,6 +244,12 @@ public class PlayerChunk { - } - return null; - } -+ public static ChunkStatus getNextStatus(ChunkStatus status) { -+ if (status == ChunkStatus.FULL) { -+ return status; -+ } -+ return CHUNK_STATUSES.get(status.getStatusIndex() + 1); -+ } - // Paper end - - public CompletableFuture> getStatusFutureUnchecked(ChunkStatus chunkstatus) { -@@ -349,7 +460,7 @@ public class PlayerChunk { - } - - public int k() { -- return this.n; -+ return Math.max(1, this.n - this.chunkPriority - (isUrgent ? 20 : 0)); // Paper - allow modifying priority, subtracts 20 if urgent - } - - private void d(int i) { -@@ -441,6 +552,7 @@ public class PlayerChunk { - Chunk fullChunk = either.left().get(); - PlayerChunk.this.isFullChunkReady = true; - fullChunk.playerChunk = PlayerChunk.this; -+ this.clearChunkUrgent(); - - - } -diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 00f26ae23da65453073fc06ffec8a349ef28dd7e..6c178492b75134ba25b7730273bb550b693a7e4a 100644 ---- a/src/main/java/net/minecraft/server/PlayerChunkMap.java -+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java -@@ -291,6 +291,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - List>> list = Lists.newArrayList(); - int j = chunkcoordintpair.x; - int k = chunkcoordintpair.z; -+ PlayerChunk requestingNeighbor = this.requestingNeighbor; // Paper - - for (int l = -i; l <= i; ++l) { - for (int i1 = -i; i1 <= i; ++i1) { -@@ -308,6 +309,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - } - - ChunkStatus chunkstatus = (ChunkStatus) intfunction.apply(j1); -+ if (requestingNeighbor != null) requestingNeighbor.onNeighborRequest(playerchunk, chunkstatus); // Paper - CompletableFuture> completablefuture = playerchunk.a(chunkstatus, this); - - list.add(completablefuture); -@@ -771,23 +773,28 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - }; - - CompletableFuture chunkSaveFuture = this.world.asyncChunkTaskManager.getChunkSaveFuture(chunkcoordintpair.x, chunkcoordintpair.z); -+ PlayerChunk playerChunk = getUpdatingChunk(chunkcoordintpair.pair()); -+ boolean isBlockingMain = playerChunk != null && playerChunk.isUrgent; -+ int priority = isBlockingMain ? com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY : com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY; - if (chunkSaveFuture != null) { -- this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z, -- com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY, chunkHolderConsumer, false, chunkSaveFuture); -- this.world.asyncChunkTaskManager.raisePriority(chunkcoordintpair.x, chunkcoordintpair.z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY); -+ this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z, priority, chunkHolderConsumer, isBlockingMain, chunkSaveFuture); - } else { -- this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z, -- com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY, chunkHolderConsumer, false); -+ this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z, priority, chunkHolderConsumer, isBlockingMain); - } -+ this.world.asyncChunkTaskManager.raisePriority(chunkcoordintpair.x, chunkcoordintpair.z, priority); - return ret; - // Paper end - } - -+ private PlayerChunk requestingNeighbor; // Paper - private CompletableFuture> b(PlayerChunk playerchunk, ChunkStatus chunkstatus) { - ChunkCoordIntPair chunkcoordintpair = playerchunk.i(); -+ PlayerChunk prevNeighbor = requestingNeighbor; // Paper -+ this.requestingNeighbor = playerchunk; // Paper - CompletableFuture, PlayerChunk.Failure>> completablefuture = this.a(chunkcoordintpair, chunkstatus.f(), (i) -> { - return this.a(chunkstatus, i); - }); -+ this.requestingNeighbor = prevNeighbor; // Paper - - this.world.getMethodProfiler().c(() -> { - return "chunkGenerate " + chunkstatus.d(); -@@ -815,6 +822,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - return CompletableFuture.completedFuture(Either.right(playerchunk_failure)); - }); - }, (runnable) -> { -+ playerchunk.onNeighborsDone(); // Paper - this.mailboxWorldGen.a(ChunkTaskQueueSorter.a(playerchunk, runnable)); // CraftBukkit - decompile error - }); - } -@@ -827,6 +835,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - })); - } - -+ public ChunkStatus getNeededStatusByRadius(ChunkStatus chunkstatus, int i) { return a(chunkstatus, i); } // Paper - OBFHELPER - private ChunkStatus a(ChunkStatus chunkstatus, int i) { - ChunkStatus chunkstatus1; - -@@ -951,9 +960,12 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - - public CompletableFuture> a(PlayerChunk playerchunk) { - ChunkCoordIntPair chunkcoordintpair = playerchunk.i(); -+ PlayerChunk prevNeighbor = this.requestingNeighbor; // Paper -+ this.requestingNeighbor = playerchunk; // Paper - CompletableFuture, PlayerChunk.Failure>> completablefuture = this.a(chunkcoordintpair, 1, (i) -> { - return ChunkStatus.FULL; - }); -+ this.requestingNeighbor = prevNeighbor; // Paper - CompletableFuture> completablefuture1 = completablefuture.thenApplyAsync((either) -> { - return either.flatMap((list) -> { - Chunk chunk = (Chunk) list.get(list.size() / 2); -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index b184f8ed8ce86965c3ef9aef179126a4d69275e8..64c643aa15d6ea68f9dad3104cc41e412255cee3 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -1,5 +1,6 @@ - package org.bukkit.craftbukkit; - -+import com.destroystokyo.paper.io.PrioritizedTaskQueue; - import com.google.common.base.Preconditions; - import com.google.common.collect.ImmutableList; - import com.google.common.collect.ImmutableMap; -@@ -2472,10 +2473,15 @@ public class CraftWorld implements World { - } - } - -- return this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { -+ CompletableFuture future = this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { - net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) either.left().orElse(null); - return CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk()); - }, MinecraftServer.getServer()); -+ if (urgent) { -+ world.asyncChunkTaskManager.raisePriority(x, z, PrioritizedTaskQueue.HIGHEST_PRIORITY); -+ } -+ return future; -+ - } - // Paper end - diff --git a/Spigot-Server-Patches/0465-Delay-unsafe-actions-until-after-entity-ticking-is-d.patch b/Spigot-Server-Patches/0465-Delay-unsafe-actions-until-after-entity-ticking-is-d.patch index c8dd5fa06..28c023108 100644 --- a/Spigot-Server-Patches/0465-Delay-unsafe-actions-until-after-entity-ticking-is-d.patch +++ b/Spigot-Server-Patches/0465-Delay-unsafe-actions-until-after-entity-ticking-is-d.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Delay unsafe actions until after entity ticking is done This will help prevent many cases of unregistering entities during entity ticking diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 9b5f24c262edb82a424385f8f3cb6aa506c0dcd9..b3785775ecd8e3c13e7829f641f2c1b5fd0d9d47 100644 +index a4a2882d32d0167738f8367209dbfd3ca4f5b953..9e32e2db10f5faaa3c5f4adc5cbc2c1a2e4d3073 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -60,6 +60,16 @@ public class WorldServer extends World { diff --git a/Spigot-Server-Patches/0467-Improved-Watchdog-Support.patch b/Spigot-Server-Patches/0467-Improved-Watchdog-Support.patch index 2e13f74e7..73f936d9b 100644 --- a/Spigot-Server-Patches/0467-Improved-Watchdog-Support.patch +++ b/Spigot-Server-Patches/0467-Improved-Watchdog-Support.patch @@ -87,7 +87,7 @@ index cfe43e882e524b6ab3d9702e81269c97e6b75eba..2632c7c3ec77918be7979f2aa49209e5 } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 10f7283cbf6f7763186ca0dcc9eb6ba8574264a7..4e9f331975e9f1f754875db25a6c6f99ed099327 100644 +index b701db638370c0d07d5be0f61c6cbf19168cde8e..4ea3468614df36e1c148a44bb15d2201da281df3 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -144,6 +144,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>(); -@@ -974,7 +976,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -962,7 +964,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { return Either.left(chunk); }); }, (runnable) -> { diff --git a/Spigot-Server-Patches/0482-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch b/Spigot-Server-Patches/0482-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch index 37a90ee53..6303099e5 100644 --- a/Spigot-Server-Patches/0482-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch +++ b/Spigot-Server-Patches/0482-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch @@ -7,7 +7,7 @@ Subject: [PATCH] Don't crash if player is attempted to be removed from I suspect it deals with teleporting as it uses players current x/y/z diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java -index 0244768f76d83af427a07bd235c9bcec8ad5429e..279c7a85fb5b4bff91fba1c9797c902bd68d8539 100644 +index 682a64c775adc1254f12d9f93b23375735ed4895..534bb87caf88f0f1bf7988494274b762ab7210e1 100644 --- a/src/main/java/net/minecraft/server/ChunkMapDistance.java +++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java @@ -226,6 +226,7 @@ public abstract class ChunkMapDistance { diff --git a/Spigot-Server-Patches/0483-Broadcast-join-message-to-console.patch b/Spigot-Server-Patches/0483-Broadcast-join-message-to-console.patch index 059381ccb..9dc5529fd 100644 --- a/Spigot-Server-Patches/0483-Broadcast-join-message-to-console.patch +++ b/Spigot-Server-Patches/0483-Broadcast-join-message-to-console.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Broadcast join message to console diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java -index 62891d2dc6f40bb57e92dfefcbcdf72f89ba5c4f..e79a4d9ff80390a67656d187b1e6ee0a83748918 100644 +index 78040e83899f1ef1a6d5c456beb9d13959307c18..c491612267977fb331368825e0d87b2fc297e9c5 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -188,9 +188,9 @@ public abstract class PlayerList { diff --git a/Spigot-Server-Patches/0484-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch b/Spigot-Server-Patches/0484-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch index e8eafe7c4..4702f6141 100644 --- a/Spigot-Server-Patches/0484-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch +++ b/Spigot-Server-Patches/0484-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch @@ -28,7 +28,7 @@ receives a deterministic result, and should no longer require 1 tick delays anymore. diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index bf2ba0548d93280651d89c2039a298c56a83b0bf..45df816980cd880b257632f0c4a381d475d2dfbd 100644 +index 64e00275edf38739fe6e2d79dbcb93243e765678..a87aa07b17205b52e85f7d082fa4d5169771cbb4 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -100,6 +100,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @@ -40,10 +40,10 @@ index bf2ba0548d93280651d89c2039a298c56a83b0bf..45df816980cd880b257632f0c4a381d4 // CraftBukkit end public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 79133dd40c465224b1dfa2afaf4c8d3f5a1c2190..ea0086ceb64ad88a64b8327f055836afb3191a0b 100644 +index 350ce63eb19b8e999c2da00d8235e1760dc948fc..047d6ccdc85363b27f9c7d78c4281fdf4ade1087 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java -@@ -1526,6 +1526,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1514,6 +1514,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { .printStackTrace(); return; } @@ -52,7 +52,7 @@ index 79133dd40c465224b1dfa2afaf4c8d3f5a1c2190..ea0086ceb64ad88a64b8327f055836af if (!(entity instanceof EntityComplexPart)) { if (!(entity instanceof EntityLightning)) { diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java -index e79a4d9ff80390a67656d187b1e6ee0a83748918..6a4e4f93eb36ca388523a36abf38bcae3ad375e8 100644 +index c491612267977fb331368825e0d87b2fc297e9c5..40f5d9fa9069a330b6999eefa50015daa4c19217 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -177,6 +177,12 @@ public abstract class PlayerList { diff --git a/Spigot-Server-Patches/0485-Load-Chunks-for-Login-Asynchronously.patch b/Spigot-Server-Patches/0485-Load-Chunks-for-Login-Asynchronously.patch index 191a97c0b..05239ea2a 100644 --- a/Spigot-Server-Patches/0485-Load-Chunks-for-Login-Asynchronously.patch +++ b/Spigot-Server-Patches/0485-Load-Chunks-for-Login-Asynchronously.patch @@ -18,7 +18,7 @@ index f20f798f0f7bb765ffdab8672f4bf77a60fa52d2..9eab570e48817e18d10ddde95b3f80f7 public void setPositionRotation(BlockPosition blockposition, float f, float f1) { diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 45df816980cd880b257632f0c4a381d475d2dfbd..48bbaec4b64bede8d280bd866436f5528578013e 100644 +index a87aa07b17205b52e85f7d082fa4d5169771cbb4..79c2187b7383336e7574709e6d4ad805e557976f 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -43,6 +43,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @@ -60,7 +60,7 @@ index f1222fcb2bd52b8781d0f92c94e1472fa7b1e493..28f48f22522ef8c3c66381abcf017f08 if (entityplayer != null) { this.g = LoginListener.EnumProtocolState.DELAY_ACCEPT; diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index f675892e45b033cac5cbb6d86b70ba38bf67ebb3..51d6ef69e7256dda2491837b5edf8f83cd0346ea 100644 +index 1673e5b8f13978cd4a47555b9806d801cbbdf3c8..56bace816f5ea5b8b837df7e7707796f68b76f37 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -69,6 +69,7 @@ public class PlayerConnection implements PacketListenerPlayIn { @@ -97,7 +97,7 @@ index f675892e45b033cac5cbb6d86b70ba38bf67ebb3..51d6ef69e7256dda2491837b5edf8f83 this.minecraftServer.getMethodProfiler().enter("keepAlive"); // Paper Start - give clients a longer time to respond to pings as per pre 1.12.2 timings diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java -index e5177bb6a20e5f5952b2fcad201a166c2c51510b..9a9c124e6af1fb40255ffaa38d0a4e024ad72436 100644 +index 40f5d9fa9069a330b6999eefa50015daa4c19217..60af90bf8c376ab8ab61b16ae38886149faa88cc 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -52,11 +52,12 @@ public abstract class PlayerList { diff --git a/Spigot-Server-Patches/0487-Allow-sleeping-players-to-float.patch b/Spigot-Server-Patches/0487-Allow-sleeping-players-to-float.patch index 580443266..c8a650af8 100644 --- a/Spigot-Server-Patches/0487-Allow-sleeping-players-to-float.patch +++ b/Spigot-Server-Patches/0487-Allow-sleeping-players-to-float.patch @@ -9,7 +9,7 @@ their position to the ground/exit location when entering the bed, resulting in the server believing they're still in the air. diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index d77b28ab4a5fc93a325a0d5594f66f4f143bd318..0ad23cd1f79621e64fa25582b36b1b26301e2264 100644 +index 56bace816f5ea5b8b837df7e7707796f68b76f37..148cd6a850ef87095558d008fbaf0038d537b78a 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -160,7 +160,7 @@ public class PlayerConnection implements PacketListenerPlayIn { diff --git a/Spigot-Server-Patches/0489-Allow-multiple-callbacks-to-schedule-for-Callback-Ex.patch b/Spigot-Server-Patches/0489-Allow-multiple-callbacks-to-schedule-for-Callback-Ex.patch index 340d0e054..866f148aa 100644 --- a/Spigot-Server-Patches/0489-Allow-multiple-callbacks-to-schedule-for-Callback-Ex.patch +++ b/Spigot-Server-Patches/0489-Allow-multiple-callbacks-to-schedule-for-Callback-Ex.patch @@ -14,7 +14,7 @@ Use an ArrayDeque to store this Queue We make sure to also implement a pattern that is recursion safe too. diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index ea0086ceb64ad88a64b8327f055836afb3191a0b..bdf835397aa691c41280f65a7785e777791b2891 100644 +index 047d6ccdc85363b27f9c7d78c4281fdf4ade1087..f81eeb7e86018d47312170bda1b4b76697943d69 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -87,24 +87,32 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { diff --git a/Spigot-Server-Patches/0493-Implement-Brigadier-Mojang-API.patch b/Spigot-Server-Patches/0493-Implement-Brigadier-Mojang-API.patch index 257973d35..55eb3c5e4 100644 --- a/Spigot-Server-Patches/0493-Implement-Brigadier-Mojang-API.patch +++ b/Spigot-Server-Patches/0493-Implement-Brigadier-Mojang-API.patch @@ -69,7 +69,7 @@ index 0b23a0548d52a30c064d624e39a896a9791aab3b..c988c929f1063b417d10d84b0c131277 public boolean hasPermission(int i) { // CraftBukkit start diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index 0deb0c17259d53c12f0e26e19a0a08666e38f871..0fd2c17f8c4b556364e359b72d6e56a5c93d727f 100644 +index 148cd6a850ef87095558d008fbaf0038d537b78a..63cbdc3531712b9b63f75789c3481a74a44a0fa7 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -581,8 +581,12 @@ public class PlayerConnection implements PacketListenerPlayIn { diff --git a/Spigot-Server-Patches/0495-Validate-PickItem-Packet-and-kick-for-invalid.patch b/Spigot-Server-Patches/0495-Validate-PickItem-Packet-and-kick-for-invalid.patch index 889cf6cd2..493de8f33 100644 --- a/Spigot-Server-Patches/0495-Validate-PickItem-Packet-and-kick-for-invalid.patch +++ b/Spigot-Server-Patches/0495-Validate-PickItem-Packet-and-kick-for-invalid.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Validate PickItem Packet and kick for invalid diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index 0fd2c17f8c4b556364e359b72d6e56a5c93d727f..066438f4ace35310d363e856a22e2f8b48445768 100644 +index 63cbdc3531712b9b63f75789c3481a74a44a0fa7..625479b97adf19df67f04dcbe445d78bccb0fc2c 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -696,7 +696,14 @@ public class PlayerConnection implements PacketListenerPlayIn { diff --git a/Spigot-Server-Patches/0502-Use-distance-map-to-optimise-entity-tracker.patch b/Spigot-Server-Patches/0502-Use-distance-map-to-optimise-entity-tracker.patch index 5894f56cc..c477dfa38 100644 --- a/Spigot-Server-Patches/0502-Use-distance-map-to-optimise-entity-tracker.patch +++ b/Spigot-Server-Patches/0502-Use-distance-map-to-optimise-entity-tracker.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Use distance map to optimise entity tracker Use the distance map to find candidate players for tracking. diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 6dea557fa32fd44674bf01e2f7429c9691c315b8..c005734f835fd07b121fdb885d981e55f8be81b4 100644 +index 32daf027a3575d73aeabf9db14a2e0c74e4cc7e6..b176dc26d15065aebc91c75e8a96745f589c0b87 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -245,6 +245,21 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke @@ -44,7 +44,7 @@ index 3a88c9a67062eb73ad8257ea786efca7e7e99f65..6d3b34ead9cc95dcc1152dffa8c6c4a8 List list = this.tracker.getPassengers(); diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index bdf835397aa691c41280f65a7785e777791b2891..3d31f00a22233bd885496d7aac34eb2b634c40e8 100644 +index f81eeb7e86018d47312170bda1b4b76697943d69..c02e127b1e98a8603d426cfb7f46532427227e67 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -120,21 +120,51 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -145,7 +145,7 @@ index bdf835397aa691c41280f65a7785e777791b2891..3d31f00a22233bd885496d7aac34eb2b } public void updatePlayerMobTypeMap(Entity entity) { -@@ -1423,17 +1491,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1411,17 +1479,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { } public void movePlayer(EntityPlayer entityplayer) { @@ -164,7 +164,7 @@ index bdf835397aa691c41280f65a7785e777791b2891..3d31f00a22233bd885496d7aac34eb2b int i = MathHelper.floor(entityplayer.locX()) >> 4; int j = MathHelper.floor(entityplayer.locZ()) >> 4; -@@ -1550,7 +1608,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1538,7 +1596,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { entity.tracker = playerchunkmap_entitytracker; // Paper - Fast access to tracker this.trackedEntities.put(entity.getId(), playerchunkmap_entitytracker); @@ -173,7 +173,7 @@ index bdf835397aa691c41280f65a7785e777791b2891..3d31f00a22233bd885496d7aac34eb2b if (entity instanceof EntityPlayer) { EntityPlayer entityplayer = (EntityPlayer) entity; -@@ -1594,7 +1652,37 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1582,7 +1640,37 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { entity.tracker = null; // Paper - We're no longer tracked } @@ -211,7 +211,7 @@ index bdf835397aa691c41280f65a7785e777791b2891..3d31f00a22233bd885496d7aac34eb2b List list = Lists.newArrayList(); List list1 = this.world.getPlayers(); -@@ -1662,23 +1750,31 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1650,23 +1738,31 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { PacketDebug.a(this.world, chunk.getPos()); List list = Lists.newArrayList(); List list1 = Lists.newArrayList(); @@ -255,7 +255,7 @@ index bdf835397aa691c41280f65a7785e777791b2891..3d31f00a22233bd885496d7aac34eb2b Iterator iterator; Entity entity1; -@@ -1716,7 +1812,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1704,7 +1800,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { public class EntityTracker { @@ -264,7 +264,7 @@ index bdf835397aa691c41280f65a7785e777791b2891..3d31f00a22233bd885496d7aac34eb2b private final Entity tracker; private final int trackingDistance; private SectionPosition e; -@@ -1733,6 +1829,42 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1721,6 +1817,42 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { this.e = SectionPosition.a(entity); } @@ -307,7 +307,7 @@ index bdf835397aa691c41280f65a7785e777791b2891..3d31f00a22233bd885496d7aac34eb2b public boolean equals(Object object) { return object instanceof PlayerChunkMap.EntityTracker ? ((PlayerChunkMap.EntityTracker) object).tracker.getId() == this.tracker.getId() : false; } -@@ -1829,7 +1961,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1817,7 +1949,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { int j = entity.getEntityType().getChunkRange() * 16; j = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, j); // Paper diff --git a/Spigot-Server-Patches/0503-Optimize-isOutsideRange-to-use-distance-maps.patch b/Spigot-Server-Patches/0503-Optimize-isOutsideRange-to-use-distance-maps.patch index 5c32f0874..9dc11e6e5 100644 --- a/Spigot-Server-Patches/0503-Optimize-isOutsideRange-to-use-distance-maps.patch +++ b/Spigot-Server-Patches/0503-Optimize-isOutsideRange-to-use-distance-maps.patch @@ -77,10 +77,10 @@ index 534bb87caf88f0f1bf7988494274b762ab7210e1..b98abed74f214932b4a226f12645f987 public String c() { diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 99be314307071e7b85ba444dcab923dd4edf0ddb..8ca6adbac2d2e43bf597c405134c1c5c1b430e63 100644 +index e89683b4f1e3cac60b88a5c7317e525c46950b17..0a99b347d8497f097ef1da6560a5d0adc1374f25 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -737,6 +737,36 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -724,6 +724,36 @@ public class ChunkProviderServer extends IChunkProvider { boolean flag1 = this.world.getGameRules().getBoolean(GameRules.DO_MOB_SPAWNING) && !world.getPlayers().isEmpty(); // CraftBukkit if (!flag) { @@ -117,7 +117,7 @@ index 99be314307071e7b85ba444dcab923dd4edf0ddb..8ca6adbac2d2e43bf597c405134c1c5c this.world.getMethodProfiler().enter("pollingChunks"); int k = this.world.getGameRules().getInt(GameRules.RANDOM_TICK_SPEED); BlockPosition blockposition = this.world.getSpawn(); -@@ -771,15 +801,7 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -758,15 +788,7 @@ public class ChunkProviderServer extends IChunkProvider { this.world.timings.countNaturalMobs.stopTiming(); // Paper - timings this.world.getMethodProfiler().exit(); @@ -134,7 +134,7 @@ index 99be314307071e7b85ba444dcab923dd4edf0ddb..8ca6adbac2d2e43bf597c405134c1c5c final int[] chunksTicked = {0}; this.playerChunkMap.forEachVisibleChunk((playerchunk) -> { // Paper - safe iterator incase chunk loads, also no wrapping Optional optional = ((Either) playerchunk.b().getNow(PlayerChunk.UNLOADED_CHUNK)).left(); -@@ -793,10 +815,10 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -780,10 +802,10 @@ public class ChunkProviderServer extends IChunkProvider { this.world.getMethodProfiler().exit(); ChunkCoordIntPair chunkcoordintpair = playerchunk.i(); @@ -161,12 +161,12 @@ index 79c2187b7383336e7574709e6d4ad805e557976f..0560eca744cb2032bb6a3faf5aeafa95 super((World) worldserver, gameprofile); playerinteractmanager.player = this; diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index a6e7bcf79df9f4ad5b0a779f3eecf85f121bedc8..0f303be3c3257548d1888ddbb575ba69ba12d0b8 100644 +index 3d610e41969768da0d2848fa1ae195035ccfd660..c4aad20a2952cc34e334ba665a6e0910d5609497 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -148,6 +148,18 @@ public class PlayerChunk { - } - // Paper end +@@ -43,6 +43,18 @@ public class PlayerChunk { + long lastAutoSaveTime; // Paper - incremental autosave + long inactiveTimeStart; // Paper - incremental autosave + // Paper start - optimise isOutsideOfRange + // cached here to avoid a map lookup @@ -183,7 +183,7 @@ index a6e7bcf79df9f4ad5b0a779f3eecf85f121bedc8..0f303be3c3257548d1888ddbb575ba69 public PlayerChunk(ChunkCoordIntPair chunkcoordintpair, int i, LightEngine lightengine, PlayerChunk.c playerchunk_c, PlayerChunk.d playerchunk_d) { this.statusFutures = new AtomicReferenceArray(PlayerChunk.CHUNK_STATUSES.size()); this.fullChunkFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE; -@@ -164,6 +176,7 @@ public class PlayerChunk { +@@ -59,6 +71,7 @@ public class PlayerChunk { this.n = this.oldTicketLevel; this.a(i); this.chunkMap = (PlayerChunkMap)playerchunk_d; // Paper @@ -192,7 +192,7 @@ index a6e7bcf79df9f4ad5b0a779f3eecf85f121bedc8..0f303be3c3257548d1888ddbb575ba69 // Paper start diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 3d31f00a22233bd885496d7aac34eb2b634c40e8..b3c9cb67664491c3a8c83a67ac0e79d48561f3fe 100644 +index c02e127b1e98a8603d426cfb7f46532427227e67..9d414b6bbdda2a4d5a4ecdad6abb7d53860e7e71 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -130,6 +130,17 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -300,7 +300,7 @@ index 3d31f00a22233bd885496d7aac34eb2b634c40e8..b3c9cb67664491c3a8c83a67ac0e79d4 private static double a(ChunkCoordIntPair chunkcoordintpair, Entity entity) { double d0 = (double) (chunkcoordintpair.x * 16 + 8); double d1 = (double) (chunkcoordintpair.z * 16 + 8); -@@ -439,6 +493,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -437,6 +491,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { } else { if (playerchunk != null) { playerchunk.a(j); @@ -308,7 +308,7 @@ index 3d31f00a22233bd885496d7aac34eb2b634c40e8..b3c9cb67664491c3a8c83a67ac0e79d4 } if (playerchunk != null) { -@@ -1420,30 +1475,53 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1408,30 +1463,53 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { return isOutsideOfRange(chunkcoordintpair, false); } diff --git a/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch b/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch index 8d1a26b87..cf9b8030b 100644 --- a/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch +++ b/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch @@ -114,10 +114,20 @@ index 0560eca744cb2032bb6a3faf5aeafa95a7a6815e..07a6fc3d88e7d44bfab7f3d6a0eef7dc super((World) worldserver, gameprofile); playerinteractmanager.player = this; diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 0f303be3c3257548d1888ddbb575ba69ba12d0b8..7b2a3287ce8d296d29cbef45322a469921cf9944 100644 +index c4aad20a2952cc34e334ba665a6e0910d5609497..845e5d2a8ee025ac61cf916de04e0797e32db568 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -160,6 +160,18 @@ public class PlayerChunk { +@@ -47,14 +47,28 @@ public class PlayerChunk { + // cached here to avoid a map lookup + com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet playersInMobSpawnRange; + com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet playersInChunkTickRange; ++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet playersInTickingRange; + + void updateRanges() { + long key = net.minecraft.server.MCUtil.getCoordinateKey(this.location); + this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key); + this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key); ++ this.playersInTickingRange = this.chunkMap.playerViewDistanceTickMap.getObjectsInRange(key); } // Paper end - optimise isOutsideOfRange @@ -136,7 +146,7 @@ index 0f303be3c3257548d1888ddbb575ba69ba12d0b8..7b2a3287ce8d296d29cbef45322a4699 public PlayerChunk(ChunkCoordIntPair chunkcoordintpair, int i, LightEngine lightengine, PlayerChunk.c playerchunk_c, PlayerChunk.d playerchunk_d) { this.statusFutures = new AtomicReferenceArray(PlayerChunk.CHUNK_STATUSES.size()); this.fullChunkFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE; -@@ -321,7 +333,7 @@ public class PlayerChunk { +@@ -210,7 +224,7 @@ public class PlayerChunk { } public void a(int i, int j, int k) { @@ -145,7 +155,7 @@ index 0f303be3c3257548d1888ddbb575ba69ba12d0b8..7b2a3287ce8d296d29cbef45322a4699 if (chunk != null) { this.r |= 1 << (j >> 4); -@@ -341,7 +353,7 @@ public class PlayerChunk { +@@ -230,7 +244,7 @@ public class PlayerChunk { } public void a(EnumSkyBlock enumskyblock, int i) { @@ -154,7 +164,7 @@ index 0f303be3c3257548d1888ddbb575ba69ba12d0b8..7b2a3287ce8d296d29cbef45322a4699 if (chunk != null) { chunk.setNeedsSaving(true); -@@ -426,9 +438,48 @@ public class PlayerChunk { +@@ -315,9 +329,48 @@ public class PlayerChunk { } private void a(Packet packet, boolean flag) { @@ -207,7 +217,7 @@ index 0f303be3c3257548d1888ddbb575ba69ba12d0b8..7b2a3287ce8d296d29cbef45322a4699 public CompletableFuture> a(ChunkStatus chunkstatus, PlayerChunkMap playerchunkmap) { diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index b3c9cb67664491c3a8c83a67ac0e79d48561f3fe..9071080df09a67a5c07545a426b9096ef319a753 100644 +index 9d414b6bbdda2a4d5a4ecdad6abb7d53860e7e71..abfe75750ea564cd56b53d3f09eb247478384e2f 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -71,7 +71,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -340,7 +350,7 @@ index b3c9cb67664491c3a8c83a67ac0e79d48561f3fe..9071080df09a67a5c07545a426b9096e } public void updatePlayerMobTypeMap(Entity entity) { -@@ -1113,15 +1199,11 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1101,15 +1187,11 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { completablefuture1.thenAcceptAsync((either) -> { either.mapLeft((chunk) -> { this.u.getAndIncrement(); @@ -358,7 +368,7 @@ index b3c9cb67664491c3a8c83a67ac0e79d48561f3fe..9071080df09a67a5c07545a426b9096e }); return completablefuture1; } -@@ -1221,32 +1303,38 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1209,32 +1291,38 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { } // Paper } @@ -412,7 +422,7 @@ index b3c9cb67664491c3a8c83a67ac0e79d48561f3fe..9071080df09a67a5c07545a426b9096e protected void sendChunk(EntityPlayer entityplayer, ChunkCoordIntPair chunkcoordintpair, Packet[] apacket, boolean flag, boolean flag1) { if (entityplayer.world == this.world) { -@@ -1254,7 +1342,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1242,7 +1330,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { PlayerChunk playerchunk = this.getVisibleChunk(chunkcoordintpair.pair()); if (playerchunk != null) { @@ -421,7 +431,7 @@ index b3c9cb67664491c3a8c83a67ac0e79d48561f3fe..9071080df09a67a5c07545a426b9096e if (chunk != null) { this.a(entityplayer, apacket, chunk); -@@ -1523,6 +1611,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1511,6 +1599,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { } // Paper end - optimise isOutsideOfRange @@ -429,7 +439,7 @@ index b3c9cb67664491c3a8c83a67ac0e79d48561f3fe..9071080df09a67a5c07545a426b9096e private boolean b(EntityPlayer entityplayer) { return entityplayer.isSpectator() && !this.world.getGameRules().getBoolean(GameRules.SPECTATORS_GENERATE_CHUNKS); } -@@ -1550,13 +1639,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1538,13 +1627,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { this.removePlayerFromDistanceMaps(entityplayer); // Paper - distance maps } @@ -444,7 +454,7 @@ index b3c9cb67664491c3a8c83a67ac0e79d48561f3fe..9071080df09a67a5c07545a426b9096e } -@@ -1564,7 +1647,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1552,7 +1635,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { SectionPosition sectionposition = SectionPosition.a((Entity) entityplayer); entityplayer.a(sectionposition); @@ -453,7 +463,7 @@ index b3c9cb67664491c3a8c83a67ac0e79d48561f3fe..9071080df09a67a5c07545a426b9096e return sectionposition; } -@@ -1609,6 +1692,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1597,6 +1680,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { int k1; int l1; @@ -461,7 +471,7 @@ index b3c9cb67664491c3a8c83a67ac0e79d48561f3fe..9071080df09a67a5c07545a426b9096e if (Math.abs(i1 - i) <= this.viewDistance * 2 && Math.abs(j1 - j) <= this.viewDistance * 2) { k1 = Math.min(i, i1) - this.viewDistance; l1 = Math.min(j, j1) - this.viewDistance; -@@ -1646,7 +1730,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1634,7 +1718,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { this.sendChunk(entityplayer, chunkcoordintpair1, new Packet[2], false, true); } } @@ -470,7 +480,7 @@ index b3c9cb67664491c3a8c83a67ac0e79d48561f3fe..9071080df09a67a5c07545a426b9096e this.updateMaps(entityplayer); // Paper - distance maps -@@ -1654,11 +1738,46 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1642,11 +1726,46 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @Override public Stream a(ChunkCoordIntPair chunkcoordintpair, boolean flag) { @@ -521,7 +531,7 @@ index b3c9cb67664491c3a8c83a67ac0e79d48561f3fe..9071080df09a67a5c07545a426b9096e } protected void addEntity(Entity entity) { -@@ -1818,6 +1937,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1806,6 +1925,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { } @@ -529,7 +539,7 @@ index b3c9cb67664491c3a8c83a67ac0e79d48561f3fe..9071080df09a67a5c07545a426b9096e private void a(EntityPlayer entityplayer, Packet[] apacket, Chunk chunk) { if (apacket[0] == null) { apacket[0] = new PacketPlayOutMapChunk(chunk, 65535); -@@ -2003,7 +2123,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1991,7 +2111,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(this.tracker.chunkX, this.tracker.chunkZ); PlayerChunk playerchunk = PlayerChunkMap.this.getVisibleChunk(chunkcoordintpair.pair()); @@ -589,10 +599,10 @@ index 899c535c4056cd2375ab8f834f03267d405f4bda..0e6368d0fb3beccb492ae3867fb4e228 if (!this.isClientSide && (i & 1) != 0) { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 64c643aa15d6ea68f9dad3104cc41e412255cee3..874240d9dddc3150d65d56d95c459b59f07b8815 100644 +index b184f8ed8ce86965c3ef9aef179126a4d69275e8..bbfbfeed12890c9d20d78a9661ab172c901f008c 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -2488,10 +2488,39 @@ public class CraftWorld implements World { +@@ -2482,10 +2482,39 @@ public class CraftWorld implements World { // Spigot start @Override public int getViewDistance() { diff --git a/Spigot-Server-Patches/0507-Fix-Light-Command.patch b/Spigot-Server-Patches/0507-Fix-Light-Command.patch index c167ac7ef..87101d0c5 100644 --- a/Spigot-Server-Patches/0507-Fix-Light-Command.patch +++ b/Spigot-Server-Patches/0507-Fix-Light-Command.patch @@ -122,10 +122,10 @@ index ddb60e9a48e5e7225ad575240b94fda24b6b78ca..beb50f78e8b2f79d45e2e7fdc932e947 if (!SyncLoadFinder.ENABLED) { sender.sendMessage(ChatColor.RED + "This command requires the server startup flag '-Dpaper.debug-sync-loads=true' to be set."); diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 7b2a3287ce8d296d29cbef45322a469921cf9944..d0085b7459293e3e3460cfda34c67bda6e7bc324 100644 +index 845e5d2a8ee025ac61cf916de04e0797e32db568..9e95c6b54f0855ddde6db6bd3e768e87fee6c21a 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -437,6 +437,7 @@ public class PlayerChunk { +@@ -328,6 +328,7 @@ public class PlayerChunk { } @@ -134,7 +134,7 @@ index 7b2a3287ce8d296d29cbef45322a469921cf9944..d0085b7459293e3e3460cfda34c67bda // Paper start - per player view distance // there can be potential desync with player's last mapped section and the view distance map, so use the diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 9071080df09a67a5c07545a426b9096ef319a753..618831289645957a71bad3925c61af9068a05221 100644 +index abfe75750ea564cd56b53d3f09eb247478384e2f..430273c306be19d7b5af171f392236f9f0d835a8 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -72,6 +72,12 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { diff --git a/Spigot-Server-Patches/0515-Ensure-Entity-AABB-s-are-never-invalid.patch b/Spigot-Server-Patches/0515-Ensure-Entity-AABB-s-are-never-invalid.patch index 940e4508c..ed3bac400 100644 --- a/Spigot-Server-Patches/0515-Ensure-Entity-AABB-s-are-never-invalid.patch +++ b/Spigot-Server-Patches/0515-Ensure-Entity-AABB-s-are-never-invalid.patch @@ -11,7 +11,7 @@ This guarantees any time we set the entities position, we also update their AABB. diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index c005734f835fd07b121fdb885d981e55f8be81b4..c5adac15d726dd03853cef10758a21e5ce690567 100644 +index b176dc26d15065aebc91c75e8a96745f589c0b87..c81b9d814d50a026872d2711f76649c00d65888b 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -416,10 +416,11 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke diff --git a/Spigot-Server-Patches/0517-Wait-for-Async-Tasks-during-shutdown.patch b/Spigot-Server-Patches/0517-Wait-for-Async-Tasks-during-shutdown.patch index 0aaec7f46..0408c944a 100644 --- a/Spigot-Server-Patches/0517-Wait-for-Async-Tasks-during-shutdown.patch +++ b/Spigot-Server-Patches/0517-Wait-for-Async-Tasks-during-shutdown.patch @@ -10,7 +10,7 @@ Adds a 5 second grace period for any async tasks to finish and warns if any are still running after that delay just as reload does. diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 1a4bc90435d0a56ab7b607c72f28772fb92049bc..a8bb89111b9ab62f41e02a471518413346d201ac 100644 +index 4ea3468614df36e1c148a44bb15d2201da281df3..5b24de6d6071bddee642ddbc00959cf93792051e 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -739,6 +739,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant newState) -> { -@@ -1944,12 +1944,112 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1932,12 +1932,112 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { } diff --git a/Spigot-Server-Patches/0529-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch b/Spigot-Server-Patches/0529-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch new file mode 100644 index 000000000..717bb03e6 --- /dev/null +++ b/Spigot-Server-Patches/0529-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch @@ -0,0 +1,524 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sat, 11 Apr 2020 03:56:07 -0400 +Subject: [PATCH] Implement Chunk Priority / Urgency System for Chunks + +Mark chunks that are blocking main thread for world generation as urgent + +Implements a general priority system so that chunks that are sorted in +the generator queues can prioritize certain chunks over another. + +Urgent chunks will jump to the front of the line, ensuring that a +sync chunk load on an ungenerated chunk does not lag the server for +a long period of time if the servers generator queues are filled with +lots of chunks already. + +This massively reduces the lag spikes from sync chunk gens. + +Then we further prioritize loading order so nearby chunks have higher +priority than distant chunks, reducing the pressure a high no tick +view distance holds on you. + +Chunks in front of the player have higher priority, to help with +fast traveling players keep up with their movement. + +diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java +index 716d4eab382244ee9bc4855bf0f026e65ec057d6..20052dcbc71899165ac99801fa9f7753672ba997 100644 +--- a/src/main/java/net/minecraft/server/ChunkMapDistance.java ++++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java +@@ -143,7 +143,7 @@ public abstract class ChunkMapDistance { + Ticket ticket1 = (Ticket) arraysetsorted.a(ticket); // CraftBukkit - decompile error + + ticket1.a(this.currentTick); +- if (ticket.b() < j) { ++ if (ticket.b() < j || (ticket.getTicketType() == TicketType.PRIORITY && ((Ticket) ticket).getObjectReason() < j)) { // Paper - check priority tickets too + this.e.b(i, ticket.b(), true); + } + +@@ -171,6 +171,48 @@ public abstract class ChunkMapDistance { + this.addTicketAtLevel(tickettype, chunkcoordintpair, i, t0); + } + ++ // Paper start ++ public boolean markUrgent(ChunkCoordIntPair coords) { ++ return this.markHighPriority(coords, 30); ++ } ++ public boolean markHighPriority(ChunkCoordIntPair coords, int priority) { ++ priority = Math.min(30, Math.max(1, priority)); ++ Ticket ticket = new Ticket(TicketType.PRIORITY, 31, priority); ++ return this.addTicket(coords.pair(), ticket); ++ } ++ public int getChunkPriority(ChunkCoordIntPair coords) { ++ int priority = 0; ++ ArraySetSorted> tickets = this.tickets.get(coords.pair()); ++ if (tickets == null) { ++ return priority; ++ } ++ for (Ticket ticket : tickets) { ++ if (ticket.getTicketType() != TicketType.PRIORITY) { ++ continue; ++ } ++ //noinspection unchecked ++ Ticket prioTicket = (Ticket) ticket; ++ if (prioTicket.getObjectReason() > priority) { ++ priority = prioTicket.getObjectReason(); ++ } ++ } ++ return priority; ++ } ++ public void clearPriorityTickets(ChunkCoordIntPair coords) { ++ ArraySetSorted> tickets = this.tickets.get(coords.pair()); ++ java.util.List> toRemove = new java.util.ArrayList<>(); ++ if (tickets == null) return; ++ for (Ticket ticket : tickets) { ++ if (ticket.getTicketType() == TicketType.PRIORITY) { ++ toRemove.add(ticket); ++ } ++ } ++ for (Ticket ticket : toRemove) { ++ this.removeTicket(coords.pair(), ticket); ++ } ++ ++ } ++ // Paper end + public boolean addTicketAtLevel(TicketType ticketType, ChunkCoordIntPair chunkcoordintpair, int level, T identifier) { + return this.addTicket(chunkcoordintpair.pair(), new Ticket<>(ticketType, level, identifier)); + // CraftBukkit end +@@ -386,7 +428,8 @@ public abstract class ChunkMapDistance { + + }); + }, i, () -> { +- return j; ++ PlayerChunk chunk = chunkMap.getUpdatingChunk(i); // Paper ++ return chunk != null && chunk.getCurrentPriority() < j ? chunk.getCurrentPriority() : j; // Paper + })); + } else { + ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error +diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java +index 0a99b347d8497f097ef1da6560a5d0adc1374f25..d6f629d45e05167c22b6cd08a9709809a32b15a1 100644 +--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java ++++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java +@@ -431,6 +431,16 @@ public class ChunkProviderServer extends IChunkProvider { + public void removeTicketAtLevel(TicketType ticketType, ChunkCoordIntPair chunkPos, int ticketLevel, T identifier) { + this.chunkMapDistance.removeTicketAtLevel(ticketType, chunkPos, ticketLevel, identifier); + } ++ ++ public boolean markUrgent(ChunkCoordIntPair coords) { ++ return chunkMapDistance.markUrgent(coords); ++ } ++ public boolean markHighPriority(ChunkCoordIntPair coords, int priority) { ++ return chunkMapDistance.markHighPriority(coords, priority); ++ } ++ public void clearPriorityTickets(ChunkCoordIntPair coords) { ++ this.chunkMapDistance.clearPriorityTickets(coords); ++ } + // Paper end + + @Nullable +@@ -469,6 +479,8 @@ public class ChunkProviderServer extends IChunkProvider { + + if (!completablefuture.isDone()) { // Paper + // Paper start - async chunk io/loading ++ ChunkCoordIntPair pair = new ChunkCoordIntPair(x, z); ++ this.markUrgent(pair); + this.world.asyncChunkTaskManager.raisePriority(x, z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); + com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.world, x, z); + // Paper end +@@ -477,6 +489,7 @@ public class ChunkProviderServer extends IChunkProvider { + this.serverThreadQueue.awaitTasks(completablefuture::isDone); + com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug + this.world.timings.syncChunkLoad.stopTiming(); // Paper ++ this.clearPriorityTickets(pair); // Paper + } // Paper + ichunkaccess = (IChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { + return ichunkaccess1; +@@ -529,6 +542,7 @@ public class ChunkProviderServer extends IChunkProvider { + if (flag && !currentlyUnloading) { + // CraftBukkit end + this.chunkMapDistance.a(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair); ++ if (isUrgent) this.markUrgent(chunkcoordintpair); // Paper + if (this.a(playerchunk, l)) { + GameProfilerFiller gameprofilerfiller = this.world.getMethodProfiler(); + +@@ -541,8 +555,13 @@ public class ChunkProviderServer extends IChunkProvider { + } + } + } +- +- return this.a(playerchunk, l) ? PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE : playerchunk.a(chunkstatus, this.playerChunkMap); ++ // Paper start ++ CompletableFuture> future = this.a(playerchunk, l) ? PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE : playerchunk.a(chunkstatus, this.playerChunkMap); ++ if (isUrgent) { ++ future.thenAccept(either -> this.clearPriorityTickets(chunkcoordintpair)); ++ } ++ return future; ++ // Paper end + } + + private boolean a(@Nullable PlayerChunk playerchunk, int i) { +diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java +index 07a6fc3d88e7d44bfab7f3d6a0eef7dc132ab422..d60f659b368500e3a8c3305f99e60ffc643e2fbd 100644 +--- a/src/main/java/net/minecraft/server/EntityPlayer.java ++++ b/src/main/java/net/minecraft/server/EntityPlayer.java +@@ -441,6 +441,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + if (valid && (!this.isSpectator() || this.world.isLoaded(new BlockPosition(this)))) { // Paper - don't tick dead players that are not in the world currently (pending respawn) + super.tick(); + } ++ if (valid && isAlive() && this.ticksLived % 20 == 0) ((WorldServer)world).getChunkProvider().playerChunkMap.checkHighPriorityChunks(this); // Paper + + for (int i = 0; i < this.inventory.getSize(); ++i) { + ItemStack itemstack = this.inventory.getItem(i); +diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java +index d129c7f54d9f65fff6f512d8ff5f1c3866632603..9b9536fba4a62c0153b921e678e6a9683bf2e37f 100644 +--- a/src/main/java/net/minecraft/server/MCUtil.java ++++ b/src/main/java/net/minecraft/server/MCUtil.java +@@ -658,6 +658,7 @@ public final class MCUtil { + chunkData.addProperty("x", playerChunk.location.x); + chunkData.addProperty("z", playerChunk.location.z); + chunkData.addProperty("ticket-level", playerChunk.getTicketLevel()); ++ chunkData.addProperty("priority", playerChunk.getCurrentPriority()); + chunkData.addProperty("state", PlayerChunk.getChunkState(playerChunk.getTicketLevel()).toString()); + chunkData.addProperty("queued-for-unload", chunkMap.unloadQueue.contains(playerChunk.location.pair())); + chunkData.addProperty("status", status == null ? "unloaded" : status.toString()); +diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java +index 9e95c6b54f0855ddde6db6bd3e768e87fee6c21a..44721a4f446bdd5bf4575e4168a0570afd71c4d4 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunk.java ++++ b/src/main/java/net/minecraft/server/PlayerChunk.java +@@ -25,7 +25,7 @@ public class PlayerChunk { + private CompletableFuture chunkSave; + public int oldTicketLevel; + private int ticketLevel; +- private int n; ++ private int n; public final int getCurrentPriority() { return n; } // Paper - OBFHELPER + final ChunkCoordIntPair location; // Paper - private -> package + private final short[] dirtyBlocks; + private int dirtyCount; +@@ -68,6 +68,92 @@ public class PlayerChunk { + return null; + } + // Paper end - no-tick view distance ++ // Paper start - Chunk gen/load priority system ++ volatile int neighborPriority = -1; ++ final java.util.concurrent.ConcurrentHashMap neighbors = new java.util.concurrent.ConcurrentHashMap<>(); ++ final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap neighborPriorities = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(); ++ ++ public int getPreferredPriority() { ++ int priority = neighborPriority; // if we have a neighbor priority, use it ++ int priorityBoost = chunkMap.chunkDistanceManager.getChunkPriority(location); ++ int basePriority = ticketLevel - priorityBoost; ++ ++ if (priority == -1 || priority > basePriority) { ++ if (priorityBoost > 0) { ++ //System.out.println(location + " boost " + (basePriority) + " = " + ticketLevel + " - " + priorityBoost); ++ } ++ priority = basePriority; ++ if (ticketLevel >= 34 && priorityBoost == 0) { ++ priority += 5; ++ } ++ } ++ ++ ++ return Math.max(1, Math.min(PlayerChunkMap.GOLDEN_TICKET, priority)); ++ } ++ public void onNeighborRequest(PlayerChunk neighbor, ChunkStatus status) { ++ int currentPriority = getCurrentPriority(); ++ if (!neighborPriorities.containsKey(neighbor.location.pair()) && (neighbor.neighborPriority == -1 || neighbor.neighborPriority > currentPriority)) { ++ this.neighbors.put(neighbor, currentPriority); ++ neighbor.setNeighborPriority(this, Math.max(1, currentPriority)); ++ } ++ } ++ ++ private void setNeighborPriority(PlayerChunk requester, int priority) { ++ if (priority < neighborPriority || neighborPriority == -1) { ++ synchronized (neighborPriorities) { ++ if (priority < neighborPriority || neighborPriority == -1) { ++ neighborPriority = priority; ++ neighborPriorities.put(requester.location.pair(), Integer.valueOf(priority)); ++ } ++ } ++ } ++ } ++ ++ public void onNeighborsDone() { ++ java.util.List neighbors = new java.util.ArrayList<>(this.neighbors.keySet()); ++ this.neighbors.clear(); ++ for (PlayerChunk neighbor : neighbors) { ++ synchronized (neighbor.neighborPriorities) { ++ neighbor.neighborPriorities.remove(location.pair()); ++ neighbor.recalcNeighborPriority(); ++ } ++ } ++ } ++ ++ private void recalcNeighborPriority() { ++ neighborPriority = -1; ++ if (!neighborPriorities.isEmpty()) { ++ synchronized (neighborPriorities) { ++ for (Integer neighbor : neighborPriorities.values()) { ++ if (neighbor < neighborPriority || neighborPriority == -1) { ++ neighborPriority = neighbor; ++ } ++ } ++ } ++ } ++ } ++ ++ public final double getDistanceFromPointInFront(EntityPlayer player, int dist) { ++ int inFront = dist * 16; ++ final float yaw = MCUtil.normalizeYaw(player.yaw); ++ double rads = Math.toRadians(yaw); ++ final double x = player.locX() + inFront * Math.cos(rads); ++ final double z = player.locZ() + inFront * Math.sin(rads); ++ return getDistance(x, z); ++ } ++ ++ public final double getDistance(EntityPlayer player) { ++ return getDistance(player.locX(), player.locZ()); ++ } ++ public final double getDistance(double blockX, double blockZ) { ++ int cx = MCUtil.fastFloor(blockX) >> 4; ++ int cz = MCUtil.fastFloor(blockZ) >> 4; ++ final double x = location.x - cx; ++ final double z = location.z - cz; ++ return (x * x) + (z * z); ++ } ++ // Paper end + + public PlayerChunk(ChunkCoordIntPair chunkcoordintpair, int i, LightEngine lightengine, PlayerChunk.c playerchunk_c, PlayerChunk.d playerchunk_d) { + this.statusFutures = new AtomicReferenceArray(PlayerChunk.CHUNK_STATUSES.size()); +@@ -166,6 +252,12 @@ public class PlayerChunk { + } + return null; + } ++ public static ChunkStatus getNextStatus(ChunkStatus status) { ++ if (status == ChunkStatus.FULL) { ++ return status; ++ } ++ return CHUNK_STATUSES.get(status.getStatusIndex() + 1); ++ } + // Paper end + + public CompletableFuture> getStatusFutureUnchecked(ChunkStatus chunkstatus) { +@@ -419,8 +511,18 @@ public class PlayerChunk { + return this.n; + } + ++ private void setPriority(int i) { d(i); } // Paper - OBFHELPER + private void d(int i) { ++ if (i == n) return; // Paper + this.n = i; ++ // Paper start ++ this.neighbors.keySet().forEach(neighbor -> { ++ if (neighbor.getCurrentPriority() > i) { ++ neighbor.setNeighborPriority(this, i); ++ this.w.changePriority(neighbor.location, neighbor::getCurrentPriority, neighbor.getCurrentPriority(), neighbor::setPriority); ++ } ++ }); ++ // Paper end + } + + public void a(int i) { +@@ -508,6 +610,7 @@ public class PlayerChunk { + Chunk fullChunk = either.left().get(); + PlayerChunk.this.isFullChunkReady = true; + fullChunk.playerChunk = PlayerChunk.this; ++ this.chunkMap.chunkDistanceManager.clearPriorityTickets(location); + + + } +@@ -583,7 +686,7 @@ public class PlayerChunk { + this.entityTickingFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE; + } + +- this.w.a(this.location, this::k, this.ticketLevel, this::d); ++ this.w.a(this.location, this::k, getPreferredPriority(), this::d); // Paper - preferred priority + this.oldTicketLevel = this.ticketLevel; + // CraftBukkit start + // ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins. +@@ -670,6 +773,7 @@ public class PlayerChunk { + + public interface c { + ++ default void changePriority(ChunkCoordIntPair chunkcoordintpair, IntSupplier intsupplier, int i, IntConsumer intconsumer) { a(chunkcoordintpair, intsupplier, i, intconsumer); } // Paper - OBFHELPER + void a(ChunkCoordIntPair chunkcoordintpair, IntSupplier intsupplier, int i, IntConsumer intconsumer); + } + +diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java +index e772095e1c44842f743661a326c2a9a8a677ab02..5621416660d2722f26582fcecd5b61a164cd1530 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java ++++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java +@@ -352,6 +352,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, + (EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, + com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { ++ checkHighPriorityChunks(player); + if (newState.size() != 1) { + return; + } +@@ -370,7 +371,8 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + } + ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(rangeX, rangeZ); + PlayerChunkMap.this.world.getChunkProvider().removeTicketAtLevel(TicketType.PLAYER, chunkPos, 31, chunkPos); // entity ticking level, TODO check on update +- }); ++ PlayerChunkMap.this.world.getChunkProvider().clearPriorityTickets(chunkPos); ++ }, (player, prevPos, newPos) -> checkHighPriorityChunks(player)); + this.playerViewDistanceNoTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets); + this.playerViewDistanceBroadcastMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, + (EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, +@@ -387,6 +389,62 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + }); + // Paper end - no-tick view distance + } ++ // Paper start - Chunk Prioritization ++ private static final int[][] neighborMatrix = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}}; ++ public void checkHighPriorityChunks(EntityPlayer player) { ++ MCUtil.getSpiralOutChunks(new BlockPosition(player), Math.min(7, getLoadViewDistance())).forEach(coord -> { ++ PlayerChunk chunk = getUpdatingChunk(coord.pair()); ++ if (chunk == null || chunk.isFullChunkReady() || chunk.getTicketLevel() >= 34 || ++ !world.getWorldBorder().isInBounds(coord) ++ ) { ++ return; ++ } ++ ++ double dist = chunk.getDistance(player); ++ // Prioritize immediate ++ if (dist <= 5) { ++ chunkDistanceManager.markHighPriority(coord, (int) (29 - dist)); ++ return; ++ } ++ boolean hasNeighbor = false; ++ for (int[] matrix : neighborMatrix) { ++ long neighborKey = MCUtil.getCoordinateKey(coord.x + matrix[0], coord.x + matrix[1]); ++ PlayerChunk neighbor = getUpdatingChunk(neighborKey); ++ if (neighbor != null && neighbor.isFullChunkReady()) { ++ hasNeighbor = true; ++ break; ++ } ++ } ++ if (!hasNeighbor) { ++ return; ++ } ++ // Prioritize Frustum near ++ double distFront1 = chunk.getDistanceFromPointInFront(player, 2); ++ if (distFront1 <= (4*4)) { ++ if (distFront1 <= (2 * 2)) { ++ chunkDistanceManager.markHighPriority(coord, 24); ++ } else { ++ chunkDistanceManager.markHighPriority(coord, 22); ++ } ++ return; ++ } ++ // Prioritize Frustum far ++ double distFront2 = chunk.getDistanceFromPointInFront(player, 4); ++ if (distFront2 <= (3*3)) { ++ if (distFront2 <= (2 * 2)) { ++ chunkDistanceManager.markHighPriority(coord, 23); ++ } else { ++ chunkDistanceManager.markHighPriority(coord, 20); ++ } ++ return; ++ } ++ // Prioritize nearby chunks ++ if (dist <= (5*5)) { ++ chunkDistanceManager.markHighPriority(coord, (int) (16 - Math.sqrt(dist*(4D/5D)))); ++ } ++ }); ++ } ++ // Paper end + + public void updatePlayerMobTypeMap(Entity entity) { + if (!this.world.paperConfig.perPlayerMobSpawns) { +@@ -516,6 +574,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + List>> list = Lists.newArrayList(); + int j = chunkcoordintpair.x; + int k = chunkcoordintpair.z; ++ PlayerChunk requestingNeighbor = getUpdatingChunk(chunkcoordintpair.pair()); // Paper + + for (int l = -i; l <= i; ++l) { + for (int i1 = -i; i1 <= i; ++i1) { +@@ -533,6 +592,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + } + + ChunkStatus chunkstatus = (ChunkStatus) intfunction.apply(j1); ++ if (requestingNeighbor != null && requestingNeighbor != playerchunk) requestingNeighbor.onNeighborRequest(playerchunk, chunkstatus); // Paper + CompletableFuture> completablefuture = playerchunk.a(chunkstatus, this); + + list.add(completablefuture); +@@ -997,14 +1057,22 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + }; + + CompletableFuture chunkSaveFuture = this.world.asyncChunkTaskManager.getChunkSaveFuture(chunkcoordintpair.x, chunkcoordintpair.z); ++ PlayerChunk playerChunk = getUpdatingChunk(chunkcoordintpair.pair()); ++ int chunkPriority = playerChunk != null ? playerChunk.getCurrentPriority() : 33; ++ int priority = com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY; ++ ++ if (chunkPriority <= 10) { ++ priority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY; ++ } else if (chunkPriority <= 20) { ++ priority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY; ++ } ++ boolean isHighestPriority = priority == com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY; + if (chunkSaveFuture != null) { +- this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z, +- com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY, chunkHolderConsumer, false, chunkSaveFuture); +- this.world.asyncChunkTaskManager.raisePriority(chunkcoordintpair.x, chunkcoordintpair.z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY); ++ this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z, priority, chunkHolderConsumer, isHighestPriority, chunkSaveFuture); + } else { +- this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z, +- com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY, chunkHolderConsumer, false); ++ this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z, priority, chunkHolderConsumer, isHighestPriority); + } ++ this.world.asyncChunkTaskManager.raisePriority(chunkcoordintpair.x, chunkcoordintpair.z, priority); + return ret; + // Paper end + } +@@ -1041,6 +1109,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + return CompletableFuture.completedFuture(Either.right(playerchunk_failure)); + }); + }, (runnable) -> { ++ playerchunk.onNeighborsDone(); // Paper + this.mailboxWorldGen.a(ChunkTaskQueueSorter.a(playerchunk, runnable)); // CraftBukkit - decompile error + }); + } +@@ -1133,7 +1202,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + long i = playerchunk.i().pair(); + + playerchunk.getClass(); +- mailbox.a(ChunkTaskQueueSorter.a(runnable, i, playerchunk::getTicketLevel)); // CraftBukkit - decompile error ++ mailbox.a(ChunkTaskQueueSorter.a(runnable, i, playerchunk::getCurrentPriority)); // CraftBukkit - decompile error // Paper - use priority not ticket level.... + }); + } + +diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java +index 8055f5998213ab1c6c10d03d88d2b14d220a5e40..4913205c15a2b6d5ea058890b02090b494e9c177 100644 +--- a/src/main/java/net/minecraft/server/TicketType.java ++++ b/src/main/java/net/minecraft/server/TicketType.java +@@ -23,6 +23,7 @@ public class TicketType { + public static final TicketType PLUGIN_TICKET = a("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit + public static final TicketType FUTURE_AWAIT = a("future_await", Long::compareTo); // Paper + public static final TicketType ASYNC_LOAD = a("async_load", Long::compareTo); // Paper ++ public static final TicketType PRIORITY = a("priority", Integer::compareTo, 300); // Paper + + public static TicketType a(String s, Comparator comparator) { + return new TicketType<>(s, comparator, 0L); +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index bbfbfeed12890c9d20d78a9661ab172c901f008c..589926d6029ca2a4aeb4f2c7903a5f9517deebef 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -2472,10 +2472,15 @@ public class CraftWorld implements World { + } + } + +- return this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { ++ CompletableFuture future = this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { + net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) either.left().orElse(null); + return CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk()); + }, MinecraftServer.getServer()); ++ if (urgent) { ++ world.asyncChunkTaskManager.raisePriority(x, z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); ++ } ++ return future; ++ + } + // Paper end +