diff --git a/Spigot-Server-Patches/0004-MC-Utils.patch b/Spigot-Server-Patches/0004-MC-Utils.patch index 6a4038d40d..e34a6ed157 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 c7c39d887a..2cbb8d05de 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 04681c9f36..747c485c4f 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 eff839dfec..a598dc8d7b 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 4fb0cc9c90..06ae5f51f2 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 19798635be..0b47d2e3f2 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 5665ff3572..585330c1a3 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 3b16c1f78f..3721bf78dd 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 df6324af80..3acefa77c6 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 c8dd5fa06b..28c0231086 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 2e13f74e7d..73f936d9bc 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 37a90ee533..6303099e5f 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 059381ccba..9dc5529fd8 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 e8eafe7c46..4702f61413 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 191a97c0bd..05239ea2af 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 5804432666..c8a650af86 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 340d0e054d..866f148aab 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 257973d359..55eb3c5e45 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 889cf6cd23..493de8f33b 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 5894f56ccd..c477dfa38a 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 5c32f0874f..9dc11e6e54 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 8d1a26b876..cf9b8030b4 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 c167ac7ef4..87101d0c51 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 940e4508c4..ed3bac4005 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 0aaec7f464..0408c944ac 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 0000000000..717bb03e6c --- /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 +