diff --git a/build-data/reobf-mappings-patch.tiny b/build-data/reobf-mappings-patch.tiny index f5cadde..39dccf7 100644 --- a/build-data/reobf-mappings-patch.tiny +++ b/build-data/reobf-mappings-patch.tiny @@ -9,14 +9,26 @@ # of these issues. Due to the complex nature of mappings generation and the debugging difficulty involved it may take # a significant amount of time for us to track down every possible issue, so this file will likely be around and in # use - at least in some capacity - for a long time. -# -# If you are adding mappings patches which are correcting for issues in paperweight's reobf mappings generation, -# unrelated to any changes in your patches, we ask that you PR the mapping to Paper so more users can benefit rather -# than keep the fix for your own fork. If the mappings patch is there to correct reobf for changes made in your patches, -# then obviously it doesn't make any sense to PR them upstream. tiny 2 0 mojang+yarn spigot -# Workaround by MoritzR200 (see issue#87) -c net/minecraft/server/dedicated/DedicatedServerProperties net/minecraft/server/dedicated/DedicatedServerProperties - m (Ljava/lang/String;)Ljava/lang/String; lambda$new$3 d +# CraftBukkit changes type +c net/minecraft/server/level/ServerLevel net/minecraft/server/level/WorldServer + f Lnet/minecraft/world/level/storage/PrimaryLevelData; serverLevelData K + +c net/minecraft/world/level/chunk/LevelChunk net/minecraft/world/level/chunk/Chunk + f Lnet/minecraft/server/level/ServerLevel; level r + +# See mappings-patch.tiny +c net/minecraft/server/level/ChunkMap net/minecraft/server/level/PlayerChunkMap + f Lnet/minecraft/server/level/ChunkMap$ChunkDistanceManager; distanceManager F + +# Paper changes type +c net/minecraft/core/MappedRegistry net/minecraft/core/RegistryMaterials + f Lit/unimi/dsi/fastutil/objects/Reference2IntOpenHashMap; toId e + +# upstream makes a bunch of methods public that cause accidental overrides now +c net/minecraft/world/entity/Display net/minecraft/world/entity/Display + m ()I getInterpolationDelay getInterpolationDelay_ + m (I)V setInterpolationDelay setInterpolationDelay_ + m (I)V setInterpolationDuration setInterpolationDuration_ diff --git a/build.gradle.kts b/build.gradle.kts index 1a62f03..849a1d6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,5 @@ +import io.papermc.paperweight.tasks.CollectATsFromPatches + plugins { java `maven-publish` @@ -66,3 +68,8 @@ paperweight { } } } + +// Uncomment while updating for a new Minecraft version +//tasks.withType { +// extraPatchDir.set(layout.projectDirectory.dir("patches/unapplied/server")) +//} diff --git a/gradle.properties b/gradle.properties index aefb638..9cac740 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ group=org.galemc.gale -version=1.19.4-R0.1-SNAPSHOT +version=1.20-R0.1-SNAPSHOT -mcVersion=1.19.4 -paperRef=bc4a6647c99ae98c52c1c81597834be8fec6aa0d +mcVersion=1.20 +paperRef=4b5f84712ba3afab4ab9184de91d85d070584e60 org.gradle.caching=true org.gradle.parallel=true diff --git a/patches/api/0001-Hide-irrelevant-compilation-warnings.patch b/patches/api/0001-Hide-irrelevant-compilation-warnings.patch index e8d6036..92f7db0 100644 --- a/patches/api/0001-Hide-irrelevant-compilation-warnings.patch +++ b/patches/api/0001-Hide-irrelevant-compilation-warnings.patch @@ -7,10 +7,10 @@ License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) Gale - https://galemc.org diff --git a/build.gradle.kts b/build.gradle.kts -index 279a666e8ea2c07f41ee3f28b768e95dca5f0a10..cd3207095c92ab6a195f8a24dbe4afaa65bf0996 100644 +index 7d6239855a84502de4eb3328b0dcf12ac671dce4..3ba111a857dda4773f4a66dea99d516b37c64b07 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -85,6 +85,15 @@ val generateApiVersioningFile by tasks.registering { +@@ -87,6 +87,15 @@ val generateApiVersioningFile by tasks.registering { } } @@ -26,7 +26,7 @@ index 279a666e8ea2c07f41ee3f28b768e95dca5f0a10..cd3207095c92ab6a195f8a24dbe4afaa tasks.jar { from(generateApiVersioningFile.map { it.outputs.files.singleFile }) { into("META-INF/maven/${project.group}/${project.name}") -@@ -134,6 +143,8 @@ tasks.withType { +@@ -136,6 +145,8 @@ tasks.withType { into("build/docs/javadoc") } } diff --git a/patches/api/0004-SIMD-support.patch b/patches/api/0004-SIMD-support.patch index 3666f9b..a6804be 100644 --- a/patches/api/0004-SIMD-support.patch +++ b/patches/api/0004-SIMD-support.patch @@ -13,10 +13,10 @@ As part of: Pufferfish (https://github.com/pufferfish-gg/Pufferfish) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/build.gradle.kts b/build.gradle.kts -index cd3207095c92ab6a195f8a24dbe4afaa65bf0996..0719e49dde343c80d18daf82d7fed926150d7d6d 100644 +index 3ba111a857dda4773f4a66dea99d516b37c64b07..003429ead5a773c1283a732ff54a4fdc7b85618b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -91,6 +91,7 @@ tasks.withType { +@@ -93,6 +93,7 @@ tasks.withType { compilerArgs.add("-Xlint:-module") compilerArgs.add("-Xlint:-removal") compilerArgs.add("-Xlint:-dep-ann") @@ -24,7 +24,7 @@ index cd3207095c92ab6a195f8a24dbe4afaa65bf0996..0719e49dde343c80d18daf82d7fed926 } // Gale end - hide irrelevant compilation warnings -@@ -145,6 +146,7 @@ tasks.withType { +@@ -147,6 +148,7 @@ tasks.withType { } options.addStringOption("Xdoclint:none", "-quiet") // Gale - hide irrelevant compilation warnings @@ -34,7 +34,7 @@ index cd3207095c92ab6a195f8a24dbe4afaa65bf0996..0719e49dde343c80d18daf82d7fed926 // Paper start diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java new file mode 100644 -index 0000000000000000000000000000000000000000..48312d416805697257e4cd3451b8d14bab7ea252 +index 0000000000000000000000000000000000000000..3f008b41727ff1bfdf14e6d186e8d74af5b35245 --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java @@ -0,0 +1,45 @@ @@ -58,7 +58,7 @@ index 0000000000000000000000000000000000000000..48312d416805697257e4cd3451b8d14b + public static boolean canEnable(Logger logger, boolean logVectorSizesToConsole) { + try { + int javaVersion = SIMDDetection.getJavaVersion(); -+ if (!(javaVersion >= 17 && javaVersion <= 19)) { ++ if (!(javaVersion >= 17 && javaVersion <= 22)) { + return false; + } else { + SIMDDetection.testRun = true; diff --git a/patches/api/0007-Player-canSee-by-entity-UUID.patch b/patches/api/0007-Player-canSee-by-entity-UUID.patch index 97b0cde..e19ad01 100644 --- a/patches/api/0007-Player-canSee-by-entity-UUID.patch +++ b/patches/api/0007-Player-canSee-by-entity-UUID.patch @@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 88c4885569d2b8b22fce55601d50608ac8e9388c..930086e164c1c8cef8ff27009736e6962357bc2b 100644 +index 997afb6739c72529ea8226228a9905d72f77c89a..37ab594c0323a1e85c53646f07d2f4ffef62f7c9 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -3,6 +3,8 @@ package org.bukkit.entity; @@ -49,7 +49,7 @@ index 88c4885569d2b8b22fce55601d50608ac8e9388c..930086e164c1c8cef8ff27009736e696 import org.bukkit.DyeColor; import org.bukkit.Effect; import org.bukkit.GameMode; -@@ -1645,6 +1647,16 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -1672,6 +1674,16 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM @ApiStatus.Experimental public boolean canSee(@NotNull Entity entity); diff --git a/patches/api/0010-Last-tick-time-API.patch b/patches/api/0010-Last-tick-time-API.patch index 7f8d9fe..b0fdfc3 100644 --- a/patches/api/0010-Last-tick-time-API.patch +++ b/patches/api/0010-Last-tick-time-API.patch @@ -22,6 +22,31 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java +index b5042ccfad1f99cce88b3a8878da15909333c3a0..8b66e73c03ef016b99e0480b951140c1c6be6ec4 100644 +--- a/src/main/java/org/bukkit/Bukkit.java ++++ b/src/main/java/org/bukkit/Bukkit.java +@@ -2532,6 +2532,20 @@ public final class Bukkit { + } + // Paper end + ++ // Gale start - YAPFA - last tick time - API ++ public static long getLastTickTotalTime() { ++ return server.getLastTickTotalTime(); ++ } ++ ++ public static long getLastTickProperTime() { ++ return server.getLastTickProperTime(); ++ } ++ ++ public static long getLastTickOversleepTime() { ++ return server.getLastTickOversleepTime(); ++ } ++ // Gale end - YAPFA - last tick time - API ++ + @NotNull + public static Server.Spigot spigot() { + return server.spigot(); diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java index 237b837a790bb3a3e33ae5731f550b1e8b55437b..007e23a9383ab8eda12c6dffb385256215356040 100644 --- a/src/main/java/org/bukkit/Server.java diff --git a/patches/server/0001-Gale-project-setup.patch b/patches/server/0001-Gale-project-setup.patch index a27fbc2..2106297 100644 --- a/patches/server/0001-Gale-project-setup.patch +++ b/patches/server/0001-Gale-project-setup.patch @@ -7,7 +7,7 @@ License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) Gale - https://galemc.org diff --git a/build.gradle.kts b/build.gradle.kts -index d5d49bb2b47c889e12d17dc87b8c439a60b3fe67..b7dab09e5cab1098211851ecd9555c9be68071e4 100644 +index 7a17e05b630517a6861c34a4561198db62acf82a..7617fe6903713893af8f6ef6564c97549709ff26 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,8 +7,14 @@ plugins { @@ -20,7 +20,7 @@ index d5d49bb2b47c889e12d17dc87b8c439a60b3fe67..b7dab09e5cab1098211851ecd9555c9b + // Depend on own API + implementation(project(":gale-api")) + // Depend on Paper MojangAPI -+ implementation("io.papermc.paper:paper-mojangapi:1.19.3-R0.1-SNAPSHOT") { ++ implementation("io.papermc.paper:paper-mojangapi:1.20-R0.1-SNAPSHOT") { + exclude("io.papermc.paper", "paper-api") + } + // Gale end - project setup diff --git a/patches/server/0002-Dev-import-deobfuscation-fixes.patch b/patches/server/0002-Dev-import-deobfuscation-fixes.patch index 1f21177..d3d56f4 100644 --- a/patches/server/0002-Dev-import-deobfuscation-fixes.patch +++ b/patches/server/0002-Dev-import-deobfuscation-fixes.patch @@ -93,10 +93,10 @@ index 50a9f33aa31e9273c7c52d4bb2b02f0f884f7ba5..19779298b2b2ecbe30f9308dad1d8037 }); return Collections.unmodifiableCollection(list); diff --git a/src/main/java/net/minecraft/util/ExtraCodecs.java b/src/main/java/net/minecraft/util/ExtraCodecs.java -index cf64c2bed790da41a2e524e75d60a069765f035a..57fae84be749bd4a61a0c700a5cefce7122643e6 100644 +index 066d423dc908080157586555ba01053a2477d570..9402ba85ebb8260d7bd29ddf8cf871b77aa2e783 100644 --- a/src/main/java/net/minecraft/util/ExtraCodecs.java +++ b/src/main/java/net/minecraft/util/ExtraCodecs.java -@@ -228,7 +228,7 @@ public class ExtraCodecs { +@@ -247,7 +247,7 @@ public class ExtraCodecs { return ImmutableList.of(leftFunction.apply(pair), rightFunction.apply(pair)); }); Codec codec3 = RecordCodecBuilder.create((instance) -> { @@ -105,7 +105,7 @@ index cf64c2bed790da41a2e524e75d60a069765f035a..57fae84be749bd4a61a0c700a5cefce7 }).comapFlatMap((pair) -> { return combineFunction.apply((P)pair.getFirst(), (P)pair.getSecond()); }, (pair) -> { -@@ -259,7 +259,7 @@ public class ExtraCodecs { +@@ -278,7 +278,7 @@ public class ExtraCodecs { Optional> optional = dataResult.resultOrPartial(mutableObject::setValue); return optional.isPresent() ? dataResult : DataResult.error(() -> { return "(" + (String)mutableObject.getValue() + " -> using default)"; @@ -114,7 +114,7 @@ index cf64c2bed790da41a2e524e75d60a069765f035a..57fae84be749bd4a61a0c700a5cefce7 } public DataResult coApply(DynamicOps dynamicOps, A objectx, DataResult dataResult) { -@@ -542,7 +542,7 @@ public class ExtraCodecs { +@@ -565,7 +565,7 @@ public class ExtraCodecs { static record LazyInitializedCodec(Supplier> delegate) implements Codec { LazyInitializedCodec { @@ -124,7 +124,7 @@ index cf64c2bed790da41a2e524e75d60a069765f035a..57fae84be749bd4a61a0c700a5cefce7 public DataResult> decode(DynamicOps dynamicOps, T object) { diff --git a/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java b/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java -index db8eed0070a6e9949d73e94e07fea589ace09fd4..d6de68b4b0edabf37ba551b91bbe6682895b2a82 100644 +index 2e650637afede22d314bbe4dd4e91806ce88dcc3..8081a24eb5a533a55cdca3c882ccc61255b8a76c 100644 --- a/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java +++ b/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java @@ -17,6 +17,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; @@ -196,7 +196,7 @@ index db8eed0070a6e9949d73e94e07fea589ace09fd4..d6de68b4b0edabf37ba551b91bbe6682 public static final class PoorMansPalettedContainer { diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -index 5956a7759964f5e4939f062e93714fba64f53141..51ad507a3b625201ecca50bd92f8f089f3b4d60a 100644 +index c1abeb62f63d2f8fb891efec8f76c6736b8f7f75..b056e29671f0a8437387c990fc96a9f3c1d614a2 100644 --- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java +++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java @@ -182,7 +182,7 @@ public class Zoglin extends Monster implements Enemy, HoglinBase { @@ -209,7 +209,7 @@ index 5956a7759964f5e4939f062e93714fba64f53141..51ad507a3b625201ecca50bd92f8f089 protected void updateActivity() { diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -index ac75c54e897565e340b66823caeed92ba1d1641a..e44eec821eaa1cd77569814000d4aa36253fb13d 100644 +index d02ee11066fc4f07ccb110b09b86d895ff90d4f2..5d1a30e09870a5a535c71a2e44b8e6933de0c1e8 100644 --- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java @@ -70,7 +70,7 @@ public class PiglinBrute extends AbstractPiglin { @@ -221,8 +221,25 @@ index ac75c54e897565e340b66823caeed92ba1d1641a..e44eec821eaa1cd77569814000d4aa36 } @Override +diff --git a/src/main/java/net/minecraft/world/item/BrushItem.java b/src/main/java/net/minecraft/world/item/BrushItem.java +index 0279e3fc7aed675ccf66aa68a11ff94a6dc6abe6..d025232b43af3cb8dc28dff2e3e05c72e490901d 100644 +--- a/src/main/java/net/minecraft/world/item/BrushItem.java ++++ b/src/main/java/net/minecraft/world/item/BrushItem.java +@@ -82,8 +82,10 @@ public class BrushItem extends Item { + BlockEntity var18 = world.getBlockEntity(blockPos); + if (var18 instanceof BrushableBlockEntity) { + BrushableBlockEntity brushableBlockEntity = (BrushableBlockEntity)var18; +- boolean bl2 = brushableBlockEntity.brush(world.getGameTime(), player, blockHitResult.getDirection()); +- if (bl2) { ++ // Gale start - dev import deobfuscation fixes ++ boolean bl22 = brushableBlockEntity.brush(world.getGameTime(), player, blockHitResult.getDirection()); ++ if (bl22) { ++ // Gale end - dev import deobfuscation fixes + EquipmentSlot equipmentSlot = stack.equals(player.getItemBySlot(EquipmentSlot.OFFHAND)) ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND; + stack.hurtAndBreak(1, user, (userx) -> { + userx.broadcastBreakEvent(equipmentSlot); diff --git a/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java b/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java -index daee1072171769d89783a7d7ef9e5692cb9fbc20..45b0706a48c4bf44923a2590f34913b7373de8f4 100644 +index 915c1cbcc0ecf634cc76f02bba53dc82d22be698..0c05aa0d0938b19453f45adf5931c88713e3b227 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java +++ b/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java @@ -154,7 +154,7 @@ public class SurfaceRules { diff --git a/patches/server/0003-Hide-irrelevant-compilation-warnings.patch b/patches/server/0003-Hide-irrelevant-compilation-warnings.patch index 5e694e7..e6762a5 100644 --- a/patches/server/0003-Hide-irrelevant-compilation-warnings.patch +++ b/patches/server/0003-Hide-irrelevant-compilation-warnings.patch @@ -7,13 +7,13 @@ License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) Gale - https://galemc.org diff --git a/build.gradle.kts b/build.gradle.kts -index 337003cf37ee544903c0800f7118dbd5c5a40a63..0b3f1508c2d461a11cf26e9e298e15e3650daccf 100644 +index 0bf73437d6aabadc2b784ff62f2854bcfc4c2a11..b0066dc98abf596fc60ff2d4b0761f5845d5ffa5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -56,6 +56,16 @@ dependencies { } - val craftbukkitPackageVersion = "1_19_R3" // Paper + val craftbukkitPackageVersion = "1_20_R1" // Paper + +// Gale start - hide irrelevant compilation warnings +tasks.withType { diff --git a/patches/server/0005-Enable-virtual-threads-for-development-runs.patch b/patches/server/0005-Enable-virtual-threads-for-development-runs.patch index c615c1e..4761a8d 100644 --- a/patches/server/0005-Enable-virtual-threads-for-development-runs.patch +++ b/patches/server/0005-Enable-virtual-threads-for-development-runs.patch @@ -7,10 +7,10 @@ License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) Gale - https://galemc.org diff --git a/build.gradle.kts b/build.gradle.kts -index 1f7d0aa2d36b59c23a04e94dc117da987bc1a0b2..6394d18f32d190e4eee34c40c1aa9ffe65662e6b 100644 +index ab3633eb6552bde9ac6fc16d3a5feb6d1f0992bc..86a8726e033f405cb0c8b4289f21dca16431718e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -182,6 +182,10 @@ fun TaskContainer.registerRunTask( +@@ -183,6 +183,10 @@ fun TaskContainer.registerRunTask( val memoryGb = providers.gradleProperty("paper.runMemoryGb").getOrElse("2") minHeapSize = "${memoryGb}G" maxHeapSize = "${memoryGb}G" diff --git a/patches/server/0006-Gale-branding-changes.patch b/patches/server/0006-Gale-branding-changes.patch index 10c206e..5bb0537 100644 --- a/patches/server/0006-Gale-branding-changes.patch +++ b/patches/server/0006-Gale-branding-changes.patch @@ -11,7 +11,7 @@ License for non-image files: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) Gale - https://galemc.org diff --git a/build.gradle.kts b/build.gradle.kts -index 4793c65a161e951ab9329479fdf86abdb6e641f1..1f35bf1c87cd503a3b164b4824c78d4d9ef393a9 100644 +index 7400f732562df7aaf10f7059e8085df95de792f3..860a744f61db62bb95374c6e7a4ebfd2cd51f35a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -78,7 +78,7 @@ tasks.jar { @@ -194,10 +194,10 @@ index c5d5648f4ca603ef2b1df723b58f9caf4dd3c722..e45e6b44b2a8f2cdae6e0048a812b921 .completer(new ConsoleCommandCompleter(this.server)) .option(LineReader.Option.COMPLETE_IN_WORD, true); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 9f15d9dbdfa74a0640b1a2b4ff695609d4758a4c..ec7bf5c30a19215af6fe19b16de24e523de0a2ac 100644 +index fb82bb52f219e7683fe1d3c0fb3acbe2251de8d4..bc54a4d802b81877b45fc69631f09a9668b5ea78 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -925,7 +925,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop public public static Services create(YggdrasilAuthenticationService authenticationService, File rootDirectory, File userCacheFile, joptsimple.OptionSet optionSet) throws Exception { // Paper -@@ -30,7 +36,10 @@ public record Services(MinecraftSessionService sessionService, SignatureValidato +@@ -32,7 +38,10 @@ public record Services(MinecraftSessionService sessionService, ServicesKeySet se final java.nio.file.Path legacyConfigPath = ((File) optionSet.valueOf("paper-settings")).toPath(); final java.nio.file.Path configDirPath = ((File) optionSet.valueOf("paper-settings-directory")).toPath(); io.papermc.paper.configuration.PaperConfigurations paperConfigurations = io.papermc.paper.configuration.PaperConfigurations.setup(legacyConfigPath, configDirPath, rootDirectory.toPath(), (File) optionSet.valueOf("spigot-settings")); -- return new Services(minecraftSessionService, signatureValidator, gameProfileRepository, gameProfileCache, paperConfigurations); +- return new Services(minecraftSessionService, authenticationService.getServicesKeySet(), gameProfileRepository, gameProfileCache, paperConfigurations); + // Gale start - Gale configuration + GaleConfigurations galeConfigurations = GaleConfigurations.setup(configDirPath); -+ return new Services(minecraftSessionService, signatureValidator, gameProfileRepository, gameProfileCache, paperConfigurations, galeConfigurations); ++ return new Services(minecraftSessionService, authenticationService.getServicesKeySet(), gameProfileRepository, gameProfileCache, paperConfigurations, galeConfigurations); + // Gale end - Gale configuration // Paper end } - } + diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index a7e133f3495e9132a5fdae2c24f225e7b026295a..b98fb8afdadbda47db5924299d90082edf3ca1b4 100644 +index 9f422cbeaa52b3e6a0a27af4f8ad4ddb7808483f..9d54ef5f12a24131b1504566e09cd9714b93e913 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -1,20 +1,15 @@ @@ -360,10 +362,10 @@ index a7e133f3495e9132a5fdae2c24f225e7b026295a..b98fb8afdadbda47db5924299d90082e if (this.convertOldUsers()) { this.getProfileCache().save(false); // Paper diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 45804711255f04110e9509df8d60900314aa10b7..ffbc971acd9a01e693d9fc2c9ab8fab859e1df10 100644 +index 18aac3da3c88f33b1a71a5920a8daa27e9723913..513916690d2579760f59edecb1a273f475d54adf 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -130,12 +130,10 @@ import net.minecraft.world.level.chunk.storage.EntityStorage; +@@ -132,12 +132,10 @@ import net.minecraft.world.level.chunk.storage.EntityStorage; import net.minecraft.world.level.dimension.BuiltinDimensionTypes; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.dimension.end.EndDragonFight; @@ -376,7 +378,7 @@ index 45804711255f04110e9509df8d60900314aa10b7..ffbc971acd9a01e693d9fc2c9ab8fab8 import net.minecraft.world.level.gameevent.DynamicGameEventListener; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gameevent.GameEventDispatcher; -@@ -527,7 +525,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -647,7 +645,7 @@ public class ServerLevel extends Level implements WorldGenLevel { // Holder holder = worlddimension.type(); // CraftBukkit - decompile error // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error @@ -386,7 +388,7 @@ index 45804711255f04110e9509df8d60900314aa10b7..ffbc971acd9a01e693d9fc2c9ab8fab8 this.convertable = convertable_conversionsession; this.uuid = WorldUUID.getUUID(convertable_conversionsession.levelDirectory.path().toFile()); diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 973ecd50f9cb6b86c353586e84d15dcb118ccb60..63122fb74a51547e23232a5409295369ee3aa353 100644 +index 147d802d9207e358fdb2d1c7806fc2f634dcfd98..71189e98dcb71e625defe5a0bd6355597c9382e6 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -1,10 +1,7 @@ @@ -436,7 +438,7 @@ index 973ecd50f9cb6b86c353586e84d15dcb118ccb60..63122fb74a51547e23232a5409295369 // CraftBukkit end public abstract class Level implements LevelAccessor, AutoCloseable { -@@ -175,6 +167,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -174,6 +166,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable { return this.paperConfig; } // Paper end @@ -449,7 +451,7 @@ index 973ecd50f9cb6b86c353586e84d15dcb118ccb60..63122fb74a51547e23232a5409295369 public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray public final co.aikar.timings.WorldTimingsHandler timings; // Paper -@@ -274,9 +272,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -273,9 +271,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public abstract ResourceKey getTypeKey(); @@ -462,7 +464,7 @@ index 973ecd50f9cb6b86c353586e84d15dcb118ccb60..63122fb74a51547e23232a5409295369 this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index ac72aa6f4bb39aaace7c2abd7ec7ced4b955991d..1cd4198fa1cbb4e27f016463c489dbddce9c659e 100644 +index 7f3cb409e90dc0b558f583d0c83c91f5f76d6ea8..2301c9adfd70e130ee89c63481d9976cec0793e4 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -979,6 +979,7 @@ public final class CraftServer implements Server { @@ -473,7 +475,7 @@ index ac72aa6f4bb39aaace7c2abd7ec7ced4b955991d..1cd4198fa1cbb4e27f016463c489dbdd for (ServerLevel world : this.console.getAllLevels()) { // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) -@@ -2763,6 +2764,14 @@ public final class CraftServer implements Server { +@@ -2759,6 +2760,14 @@ public final class CraftServer implements Server { return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console); } diff --git a/patches/server/0016-Make-timings-calls-final.patch b/patches/server/0016-Make-timings-calls-final.patch index ff801ad..f029f5b 100644 --- a/patches/server/0016-Make-timings-calls-final.patch +++ b/patches/server/0016-Make-timings-calls-final.patch @@ -7,10 +7,10 @@ License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index d2b0f385ae241f8212e78bfe96f8c2047c96a237..205390c78e81d46b2f121dc431d876ed9d499e77 100644 +index 2fbd75595dc222c2fb784e9e356a3dce530c6776..824b9b5374e0f4ccf2205ee55540ee9fcdec3c2c 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1385,7 +1385,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop parseresults, String s, String label) { // CraftBukkit CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseresults.getContext().getSource(); @@ -63,7 +63,7 @@ index 87cc7562e4a166d078fe11b7f6980497fc0bd33e..28d78c5d593302d8fe376686db6402ca byte b1; try { -@@ -393,8 +388,6 @@ public class Commands { +@@ -395,8 +390,6 @@ public class Commands { b0 = 0; } @@ -73,10 +73,29 @@ index 87cc7562e4a166d078fe11b7f6980497fc0bd33e..28d78c5d593302d8fe376686db6402ca return b0; diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 205390c78e81d46b2f121dc431d876ed9d499e77..f53370e581b8d07d993323be5df4d737d1e13794 100644 +index 824b9b5374e0f4ccf2205ee55540ee9fcdec3c2c..a6d5dd36d297c976ad3302ae7c3c2080c978f68d 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -214,14 +214,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop tickables = Lists.newArrayList(); @@ -91,7 +110,7 @@ index 205390c78e81d46b2f121dc431d876ed9d499e77..f53370e581b8d07d993323be5df4d737 private ServerConnectionListener connection; public final ChunkProgressListenerFactory progressListenerFactory; @Nullable -@@ -335,13 +327,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0 && this.tickCount % autosavePeriod == 0; try { this.isSaving = true; -@@ -1424,7 +1394,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper @@ -200,8 +219,9 @@ index 205390c78e81d46b2f121dc431d876ed9d499e77..f53370e581b8d07d993323be5df4d737 - }); /* Drop global time updates if (this.tickCount % 20 == 0) { - this.profiler.push("timeSync"); -@@ -1543,8 +1505,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop loadResources(ResourceManager manager, RegistryAccess.Frozen dynamicRegistryManager, FeatureFlagSet enabledFeatures, Commands.CommandSelection environment, int functionPermissionLevel, Executor prepareExecutor, Executor applyExecutor) { ReloadableServerResources reloadableServerResources = new ReloadableServerResources(dynamicRegistryManager, enabledFeatures, environment, functionPermissionLevel); @@ -423,10 +443,18 @@ index a9adb3ae346e9bb2ca335a6688163eb5d7a27e6d..cae1797677a9373321f5011e87bc5d04 }).thenApply((void_) -> { return reloadableServerResources; diff --git a/src/main/java/net/minecraft/server/ServerFunctionManager.java b/src/main/java/net/minecraft/server/ServerFunctionManager.java -index 6483a1d461904a0584b6808b2f86ac7329bba963..a4e5fdd5c4667c51ecfea0e4d3139594fc366061 100644 +index 4a610b712acfe0b1b4dcf4988bc22c6a71616050..e49e1f2afdade4dc16aa5239d60c0dcfc39cfeff 100644 --- a/src/main/java/net/minecraft/server/ServerFunctionManager.java +++ b/src/main/java/net/minecraft/server/ServerFunctionManager.java -@@ -57,10 +57,7 @@ public class ServerFunctionManager { +@@ -17,7 +17,6 @@ import net.minecraft.commands.CommandFunction; + import net.minecraft.commands.CommandSourceStack; + import net.minecraft.network.chat.Component; + import net.minecraft.resources.ResourceLocation; +-import net.minecraft.util.profiling.ProfilerFiller; + import net.minecraft.world.level.GameRules; + + public class ServerFunctionManager { +@@ -58,10 +57,7 @@ public class ServerFunctionManager { } private void executeTagFunctions(Collection functions, ResourceLocation label) { @@ -437,7 +465,7 @@ index 6483a1d461904a0584b6808b2f86ac7329bba963..a4e5fdd5c4667c51ecfea0e4d3139594 Iterator iterator = functions.iterator(); while (iterator.hasNext()) { -@@ -68,8 +65,6 @@ public class ServerFunctionManager { +@@ -69,8 +65,6 @@ public class ServerFunctionManager { this.execute(customfunction, this.getGameLoopSender()); } @@ -446,34 +474,34 @@ index 6483a1d461904a0584b6808b2f86ac7329bba963..a4e5fdd5c4667c51ecfea0e4d3139594 } public int execute(CommandFunction function, CommandSourceStack source) { -@@ -175,12 +170,10 @@ public class ServerFunctionManager { +@@ -185,12 +179,10 @@ public class ServerFunctionManager { return j; } - try { + { // Gale - Purpur - remove vanilla profiler - ServerFunctionManager.QueuedCommand customfunctiondata_b = (ServerFunctionManager.QueuedCommand) this.commandQueue.removeFirst(); + ServerFunctionManager.QueuedCommand customfunctiondata_queuedcommand = (ServerFunctionManager.QueuedCommand) this.commandQueue.removeFirst(); - ProfilerFiller gameprofilerfiller = ServerFunctionManager.this.server.getProfiler(); - Objects.requireNonNull(customfunctiondata_b); -- gameprofilerfiller.push(customfunctiondata_b::toString); - this.depth = customfunctiondata_b.depth; - customfunctiondata_b.execute(ServerFunctionManager.this, this.commandQueue, i, this.tracer); - if (!this.nestedCalls.isEmpty()) { -@@ -191,8 +184,6 @@ public class ServerFunctionManager { - list.forEach(deque::addFirst); - this.nestedCalls.clear(); + Objects.requireNonNull(customfunctiondata_queuedcommand); +- gameprofilerfiller.push(customfunctiondata_queuedcommand::toString); + this.depth = customfunctiondata_queuedcommand.depth; + customfunctiondata_queuedcommand.execute(ServerFunctionManager.this, this.commandQueue, i, this.tracer); + if (this.abortCurrentDepth) { +@@ -208,8 +200,6 @@ public class ServerFunctionManager { } + + this.nestedCalls.clear(); - } finally { - ServerFunctionManager.this.server.getProfiler().pop(); } ++j; diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index fbe209a66c77c47935ad026dd3e45e682af91fd8..d7a8b1ee9c3efda2c8463d6e06e54eb59b1b4dc4 100644 +index 9209b598d7168b82574e4800056b8b9f84265dd0..31963a0931bcd3465e46b9c875e89ac56aead62d 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -76,7 +76,6 @@ import net.minecraft.server.level.progress.ChunkProgressListener; +@@ -75,7 +75,6 @@ import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.network.ServerPlayerConnection; import net.minecraft.util.CsvOutput; import net.minecraft.util.Mth; @@ -481,7 +509,7 @@ index fbe209a66c77c47935ad026dd3e45e682af91fd8..d7a8b1ee9c3efda2c8463d6e06e54eb5 import net.minecraft.util.thread.BlockableEventLoop; import net.minecraft.util.thread.ProcessorHandle; import net.minecraft.util.thread.ProcessorMailbox; -@@ -621,20 +620,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -618,20 +617,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } protected void tick(BooleanSupplier shouldKeepTicking) { @@ -503,7 +531,7 @@ index fbe209a66c77c47935ad026dd3e45e682af91fd8..d7a8b1ee9c3efda2c8463d6e06e54eb5 public boolean hasWork() { diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index ca84eddbdb1e198b899750e5f6b3eafd25ce970f..d95c3b14c06421c03c99f84efb3a5319200f8a57 100644 +index 488a253e218409b5f0b4a872cee0928578fa7582..78201998d6cbee9aa6f0bad80b726b46675393dc 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -24,7 +24,6 @@ import net.minecraft.core.SectionPos; @@ -514,7 +542,7 @@ index ca84eddbdb1e198b899750e5f6b3eafd25ce970f..d95c3b14c06421c03c99f84efb3a5319 import net.minecraft.util.thread.BlockableEventLoop; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.ai.village.poi.PoiManager; -@@ -428,16 +427,12 @@ public class ServerChunkCache extends ChunkSource { +@@ -277,16 +276,12 @@ public class ServerChunkCache extends ChunkSource { return ifLoaded; } // Paper end @@ -531,7 +559,7 @@ index ca84eddbdb1e198b899750e5f6b3eafd25ce970f..d95c3b14c06421c03c99f84efb3a5319 CompletableFuture> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor; -@@ -625,24 +620,19 @@ public class ServerChunkCache extends ChunkSource { +@@ -475,24 +470,19 @@ public class ServerChunkCache extends ChunkSource { // CraftBukkit start - modelled on below public void purgeUnload() { if (true) return; // Paper - tickets will be removed later, this behavior isn't really well accounted for by the chunk system @@ -555,8 +583,8 @@ index ca84eddbdb1e198b899750e5f6b3eafd25ce970f..d95c3b14c06421c03c99f84efb3a5319 - this.level.getProfiler().popPush("chunks"); if (tickChunks) { this.level.timings.chunks.startTiming(); // Paper - timings - this.chunkMap.playerChunkManager.tick(); // Paper - this is mostly is to account for view distance changes -@@ -651,10 +641,8 @@ public class ServerChunkCache extends ChunkSource { + this.chunkMap.level.playerChunkLoader.tick(); // Paper - replace player chunk loader - this is mostly required to account for view distance changes +@@ -501,10 +491,8 @@ public class ServerChunkCache extends ChunkSource { } this.level.timings.doChunkUnload.startTiming(); // Spigot @@ -567,7 +595,7 @@ index ca84eddbdb1e198b899750e5f6b3eafd25ce970f..d95c3b14c06421c03c99f84efb3a5319 this.clearCache(); } -@@ -700,13 +688,10 @@ public class ServerChunkCache extends ChunkSource { +@@ -550,13 +538,10 @@ public class ServerChunkCache extends ChunkSource { } // Paper end - optimize isOutisdeRange LevelData worlddata = this.level.getLevelData(); @@ -581,7 +609,7 @@ index ca84eddbdb1e198b899750e5f6b3eafd25ce970f..d95c3b14c06421c03c99f84efb3a5319 this.level.timings.countNaturalMobs.startTiming(); // Paper - timings int l = this.distanceManager.getNaturalSpawnChunkCount(); // Paper start - per player mob spawning -@@ -724,13 +709,11 @@ public class ServerChunkCache extends ChunkSource { +@@ -574,13 +559,11 @@ public class ServerChunkCache extends ChunkSource { this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings this.lastSpawnState = spawnercreature_d; @@ -595,7 +623,7 @@ index ca84eddbdb1e198b899750e5f6b3eafd25ce970f..d95c3b14c06421c03c99f84efb3a5319 boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit // Paper - only shuffle if per-player mob spawning is disabled -@@ -781,15 +764,12 @@ public class ServerChunkCache extends ChunkSource { +@@ -632,15 +615,12 @@ public class ServerChunkCache extends ChunkSource { } // Paper end - optimise chunk tick iteration this.level.timings.chunkTicks.stopTiming(); // Paper @@ -611,7 +639,7 @@ index ca84eddbdb1e198b899750e5f6b3eafd25ce970f..d95c3b14c06421c03c99f84efb3a5319 this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) { ReferenceOpenHashSet copy = this.chunkMap.needsChangeBroadcasting.clone(); -@@ -803,7 +783,6 @@ public class ServerChunkCache extends ChunkSource { +@@ -654,7 +634,6 @@ public class ServerChunkCache extends ChunkSource { } } this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing @@ -619,7 +647,7 @@ index ca84eddbdb1e198b899750e5f6b3eafd25ce970f..d95c3b14c06421c03c99f84efb3a5319 // Paper end - use set of chunks requiring updates, rather than iterating every single one loaded // Paper start - controlled flush for entity tracker packets List disabledFlushes = new java.util.ArrayList<>(this.level.players.size()); -@@ -994,7 +973,6 @@ public class ServerChunkCache extends ChunkSource { +@@ -845,7 +824,6 @@ public class ServerChunkCache extends ChunkSource { @Override protected void doRunTask(Runnable task) { @@ -628,18 +656,18 @@ index ca84eddbdb1e198b899750e5f6b3eafd25ce970f..d95c3b14c06421c03c99f84efb3a5319 } diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe980af6c516 100644 +index 513916690d2579760f59edecb1a273f475d54adf..287f6a38a6f042983c5e652ff9800fb215a91221 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -76,7 +76,6 @@ import net.minecraft.util.CsvOutput; - import net.minecraft.util.Mth; +@@ -77,7 +77,6 @@ import net.minecraft.util.Mth; import net.minecraft.util.ProgressListener; + import net.minecraft.util.RandomSource; import net.minecraft.util.Unit; -import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.util.valueproviders.IntProvider; import net.minecraft.util.valueproviders.UniformInt; import net.minecraft.world.DifficultyInstance; -@@ -525,15 +524,17 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -645,15 +644,17 @@ public class ServerLevel extends Level implements WorldGenLevel { // Holder holder = worlddimension.type(); // CraftBukkit - decompile error // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error @@ -660,7 +688,7 @@ index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe98 this.navigatingMobs = new ObjectOpenHashSet(); this.blockEvents = new ObjectLinkedOpenHashSet(); this.blockEventsToReschedule = new ArrayList(64); -@@ -631,12 +632,8 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -772,12 +773,8 @@ public class ServerLevel extends Level implements WorldGenLevel { } } // Paper end - optimise checkDespawn @@ -673,7 +701,7 @@ index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe98 this.advanceWeatherCycle(); int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); long j; -@@ -663,32 +660,24 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -804,32 +801,24 @@ public class ServerLevel extends Level implements WorldGenLevel { this.updateSkyBrightness(); this.tickTime(); @@ -706,7 +734,7 @@ index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe98 boolean flag = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players if (flag) { -@@ -696,12 +685,9 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -837,12 +826,9 @@ public class ServerLevel extends Level implements WorldGenLevel { } if (flag || this.emptyTime++ < 300) { @@ -719,7 +747,7 @@ index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe98 } org.spigotmc.ActivationRange.activateEntities(this); // Spigot -@@ -711,9 +697,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -852,9 +838,7 @@ public class ServerLevel extends Level implements WorldGenLevel { if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed entity.discard(); } else { @@ -729,7 +757,7 @@ index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe98 if (true || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) { // Paper - now always true if in the ticking list Entity entity1 = entity.getVehicle(); -@@ -725,22 +709,17 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -866,22 +850,17 @@ public class ServerLevel extends Level implements WorldGenLevel { entity.stopRiding(); } @@ -752,7 +780,7 @@ index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe98 } @Override -@@ -799,9 +778,6 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -940,9 +919,6 @@ public class ServerLevel extends Level implements WorldGenLevel { boolean flag = this.isRaining(); int j = chunkcoordintpair.getMinBlockX(); int k = chunkcoordintpair.getMinBlockZ(); @@ -762,15 +790,15 @@ index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe98 final BlockPos.MutableBlockPos blockposition = this.chunkTickMutablePosition; // Paper - use mutable to reduce allocation rate, final to force compile fail on change if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - disable thunder -@@ -831,7 +807,6 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -972,7 +948,6 @@ public class ServerLevel extends Level implements WorldGenLevel { } } - gameprofilerfiller.popPush("iceandsnow"); int l; + int i1; - if (!this.paperConfig().environment.disableIceAndSnow && this.random.nextInt(16) == 0) { // Paper - Disable ice and snow -@@ -882,7 +857,6 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1024,7 +999,6 @@ public class ServerLevel extends Level implements WorldGenLevel { } // Paper start - optimise random block ticking @@ -778,7 +806,7 @@ index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe98 timings.chunkTicksBlocks.startTiming(); // Paper if (randomTickSpeed > 0) { LevelChunkSection[] sections = chunk.getSections(); -@@ -918,7 +892,6 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1060,7 +1034,6 @@ public class ServerLevel extends Level implements WorldGenLevel { } // Paper end - optimise random block ticking timings.chunkTicksBlocks.stopTiming(); // Paper @@ -786,7 +814,7 @@ index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe98 } public Optional findLightningRod(BlockPos pos) { -@@ -1196,19 +1169,13 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1338,19 +1311,13 @@ public class ServerLevel extends Level implements WorldGenLevel { try { // Paper end - timings entity.setOldPosAndRot(); @@ -806,7 +834,7 @@ index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe98 } finally { timer.stopTiming(); } // Paper - timings Iterator iterator = entity.getPassengers().iterator(); -@@ -1237,12 +1204,6 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1379,12 +1346,6 @@ public class ServerLevel extends Level implements WorldGenLevel { // Paper end passenger.setOldPosAndRot(); ++passenger.tickCount; @@ -819,7 +847,7 @@ index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe98 // Paper start - EAR 2 if (isActive) { passenger.rideTick(); -@@ -1254,7 +1215,6 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1396,7 +1357,6 @@ public class ServerLevel extends Level implements WorldGenLevel { vehicle.positionRider(passenger); } // Paper end - EAR 2 @@ -828,10 +856,10 @@ index ffbc971acd9a01e693d9fc2c9ab8fab859e1df10..965ef505f6dc90f3e947e3561f79fe98 while (iterator.hasNext()) { diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 98df2463bf41fc736aa6a2b6ddf89e5abde6eb39..ab208c49a5ed5db359ab8a4a12ddf91ecec2ce5d 100644 +index 5fad40fa88f697108e42461c41012d5964ed7d75..f77f4cbdca0b22e0c24acbf08425e2e83ddef449 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1164,7 +1164,6 @@ public class ServerPlayer extends Player { +@@ -1205,7 +1205,6 @@ public class ServerPlayer extends Player { PortalInfo shapedetectorshape = this.findDimensionEntryPoint(worldserver); if (shapedetectorshape != null) { @@ -839,7 +867,7 @@ index 98df2463bf41fc736aa6a2b6ddf89e5abde6eb39..ab208c49a5ed5db359ab8a4a12ddf91e worldserver = shapedetectorshape.world; // CraftBukkit if (worldserver == null) { } else // CraftBukkit - empty to fall through to null to event if (resourcekey == LevelStem.OVERWORLD && worldserver.getTypeKey() == LevelStem.NETHER) { // CraftBukkit -@@ -1187,8 +1186,6 @@ public class ServerPlayer extends Player { +@@ -1228,8 +1227,6 @@ public class ServerPlayer extends Player { worldserver = ((CraftWorld) exit.getWorld()).getHandle(); // CraftBukkit end @@ -848,7 +876,7 @@ index 98df2463bf41fc736aa6a2b6ddf89e5abde6eb39..ab208c49a5ed5db359ab8a4a12ddf91e if (true) { // CraftBukkit this.isChangingDimension = true; // CraftBukkit - Set teleport invulnerability only if player changing worlds -@@ -1206,7 +1203,6 @@ public class ServerPlayer extends Player { +@@ -1246,7 +1243,6 @@ public class ServerPlayer extends Player { this.connection.teleport(exit); // CraftBukkit - use internal teleport without event this.connection.resetPosition(); worldserver.addDuringPortalTeleport(this); @@ -857,7 +885,7 @@ index 98df2463bf41fc736aa6a2b6ddf89e5abde6eb39..ab208c49a5ed5db359ab8a4a12ddf91e this.connection.send(new ClientboundPlayerAbilitiesPacket(this.getAbilities())); playerlist.sendLevelInfo(this, worldserver); diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index aa287d7f37f38d938d195114408cb6dbda59063d..4b74da1c0138ed5946c7f446be79c647c172faa5 100644 +index ae82ab0e3d8e99f87ca8465fbcbb44b5ce18bf96..3d33236932c14d651d393656b06ae8152cc89eee 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -395,7 +395,6 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic @@ -953,34 +981,34 @@ index 7ab57748b2f2aea1003d9b7e70e76c372aa1e432..47e75ec4a01f8a456ec6ebc13031c1f0 } diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 280ee1838106201f5e3ba7753caced6d030f7e55..158fecd37dedec27a930e427550a92c4db01fc8c 100644 +index 92202262eff01ae3bbeff0e6ebdcf26ad613c169..cc6bcffcc124f75bdaf4e72601d1557a173b7825 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -791,7 +791,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -797,7 +797,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { // CraftBukkit end public void baseTick() { -- this.level.getProfiler().push("entityBaseTick"); +- this.level().getProfiler().push("entityBaseTick"); if (firstTick && this instanceof net.minecraft.world.entity.NeutralMob neutralMob) neutralMob.tickInitialPersistentAnger(level); // Paper - Update last hurt when ticking this.feetBlockState = null; if (this.isPassenger() && this.getVehicle().isRemoved()) { -@@ -852,7 +851,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -858,7 +857,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { } this.firstTick = false; -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); } public void setSharedFlagOnFire(boolean onFire) { -@@ -1026,7 +1024,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -1077,7 +1075,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { } } -- this.level.getProfiler().push("move"); +- this.level().getProfiler().push("move"); if (this.stuckSpeedMultiplier.lengthSqr() > 1.0E-7D) { movement = movement.multiply(this.stuckSpeedMultiplier); this.stuckSpeedMultiplier = Vec3.ZERO; -@@ -1035,7 +1032,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -1086,7 +1083,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { // Paper start - ignore movement changes while inactive. if (isTemporarilyActive && !(this instanceof ItemEntity || this instanceof net.minecraft.world.entity.vehicle.AbstractMinecart) && movement == getDeltaMovement() && movementType == MoverType.SELF) { setDeltaMovement(Vec3.ZERO); @@ -988,183 +1016,183 @@ index 280ee1838106201f5e3ba7753caced6d030f7e55..158fecd37dedec27a930e427550a92c4 return; } // Paper end -@@ -1056,8 +1052,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -1107,8 +1103,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { this.setPos(this.getX() + vec3d1.x, this.getY() + vec3d1.y, this.getZ() + vec3d1.z); } -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("rest"); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("rest"); boolean flag = !Mth.equal(movement.x, vec3d1.x); boolean flag1 = !Mth.equal(movement.z, vec3d1.z); -@@ -1075,9 +1069,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - BlockState iblockdata = this.level.getBlockState(blockposition); +@@ -1126,9 +1120,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + BlockState iblockdata = this.level().getBlockState(blockposition); - this.checkFallDamage(vec3d1.y, this.onGround, iblockdata, blockposition); + this.checkFallDamage(vec3d1.y, this.onGround(), iblockdata, blockposition); - if (this.isRemoved()) { -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); - } else { -+ if (!this.isRemoved()) { // Gale - Purpur - remove vanilla profiler ++ if (!this.isRemoved()) { if (this.horizontalCollision) { Vec3 vec3d2 = this.getDeltaMovement(); -@@ -1216,8 +1208,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -1264,8 +1256,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { if (this.isOnFire() && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { this.setRemainingFireTicks(-this.getFireImmuneTicks()); } - -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); } } // Paper start - detailed watchdog information -@@ -2913,7 +2903,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -3033,7 +3023,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { ServerLevel worldserver1 = minecraftserver.getLevel(resourcekey); if (true && !this.isPassenger() && this.portalTime++ >= i) { // CraftBukkit -- this.level.getProfiler().push("portal"); +- this.level().getProfiler().push("portal"); this.portalTime = i; // Paper start io.papermc.paper.event.entity.EntityPortalReadyEvent event = new io.papermc.paper.event.entity.EntityPortalReadyEvent(this.getBukkitEntity(), worldserver1 == null ? null : worldserver1.getWorld(), org.bukkit.PortalType.NETHER); -@@ -2931,7 +2920,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -3051,7 +3040,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { } } // Paper // CraftBukkit end -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); } this.isInsidePortal = false; -@@ -3398,14 +3386,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -3522,14 +3510,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { } // Paper end - if (this.level instanceof ServerLevel && !this.isRemoved()) { -- this.level.getProfiler().push("changeDimension"); + if (this.level() instanceof ServerLevel && !this.isRemoved()) { +- this.level().getProfiler().push("changeDimension"); // CraftBukkit start - // this.decouple(); + // this.unRide(); if (worldserver == null) { return null; } // CraftBukkit end -- this.level.getProfiler().push("reposition"); +- this.level().getProfiler().push("reposition"); PortalInfo shapedetectorshape = (location == null) ? this.findDimensionEntryPoint(worldserver) : new PortalInfo(new Vec3(location.x(), location.y(), location.z()), Vec3.ZERO, this.yRot, this.xRot, worldserver, null); // CraftBukkit if (shapedetectorshape == null) { -@@ -3439,7 +3425,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -3563,7 +3549,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { this.unRide(); // CraftBukkit end -- this.level.getProfiler().popPush("reloading"); +- this.level().getProfiler().popPush("reloading"); // Paper start - Change lead drop timing to prevent dupe if (this instanceof Mob) { ((Mob) this).dropLeash(true, true); // Paper drop lead -@@ -3462,10 +3447,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -3586,10 +3571,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { } this.removeAfterChangingDimensions(); -- this.level.getProfiler().pop(); - ((ServerLevel) this.level).resetEmptyTime(); +- this.level().getProfiler().pop(); + ((ServerLevel) this.level()).resetEmptyTime(); worldserver.resetEmptyTime(); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); return entity; } } else { diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 95a27d28f73039693ca64601954af62028413634..511b2a8a1d985a1dd295c346572550c2c85a16d9 100644 +index 67627bbf84f5aab2872f636b1dcb6728c8494147..5cf43c4d0ce6e3af5ea6c190db0c47c35b0a350c 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -388,7 +388,6 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -406,7 +406,6 @@ public abstract class LivingEntity extends Entity implements Attackable { } super.baseTick(); -- this.level.getProfiler().push("livingEntityBaseTick"); - if (this.fireImmune() || this.level.isClientSide) { +- this.level().getProfiler().push("livingEntityBaseTick"); + if (this.fireImmune() || this.level().isClientSide) { this.clearFire(); } -@@ -490,7 +489,6 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -508,7 +507,6 @@ public abstract class LivingEntity extends Entity implements Attackable { this.yHeadRotO = this.yHeadRot; this.yRotO = this.getYRot(); this.xRotO = this.getXRot(); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); } public boolean canSpawnSoulSpeedParticle() { -@@ -3006,10 +3004,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3044,10 +3042,7 @@ public abstract class LivingEntity extends Entity implements Attackable { } this.run += (f3 - this.run) * 0.3F; -- this.level.getProfiler().push("headTurn"); +- this.level().getProfiler().push("headTurn"); f2 = this.tickHeadTurn(f1, f2); -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("rangeChecks"); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("rangeChecks"); // Paper start - stop large pitch and yaw changes from crashing the server this.yRotO += Math.round((this.getYRot() - this.yRotO) / 360.0F) * 360.0F; -@@ -3021,7 +3016,6 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3059,7 +3054,6 @@ public abstract class LivingEntity extends Entity implements Attackable { this.yHeadRotO += Math.round((this.yHeadRot - this.yHeadRotO) / 360.0F) * 360.0F; // Paper end -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); this.animStep += f2; if (this.isFallFlying()) { ++this.fallFlyTicks; -@@ -3310,19 +3304,14 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3348,19 +3342,14 @@ public abstract class LivingEntity extends Entity implements Attackable { } this.setDeltaMovement(d4, d5, d6); -- this.level.getProfiler().push("ai"); +- this.level().getProfiler().push("ai"); if (this.isImmobile()) { this.jumping = false; this.xxa = 0.0F; this.zza = 0.0F; } else if (this.isEffectiveAi()) { -- this.level.getProfiler().push("newAi"); +- this.level().getProfiler().push("newAi"); this.serverAiStep(); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); } -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("jump"); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("jump"); if (this.jumping && this.isAffectedByFluids()) { double d7; -@@ -3349,8 +3338,6 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3387,8 +3376,6 @@ public abstract class LivingEntity extends Entity implements Attackable { this.noJumpDelay = 0; } -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("travel"); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("travel"); this.xxa *= 0.98F; this.zza *= 0.98F; this.updateFallFlying(); -@@ -3366,8 +3353,6 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3415,8 +3402,6 @@ public abstract class LivingEntity extends Entity implements Attackable { + this.travel(vec3d1); } - //SpigotTimings.timerEntityAIMove.stopTiming(); // Spigot // Paper -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("freezing"); - if (!this.level.isClientSide && !this.isDeadOrDying() && !freezeLocked) { // Paper - Freeze Tick Lock API +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("freezing"); + if (!this.level().isClientSide && !this.isDeadOrDying() && !freezeLocked) { // Paper - Freeze Tick Lock API int i = this.getTicksFrozen(); -@@ -3384,15 +3369,12 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3433,15 +3418,12 @@ public abstract class LivingEntity extends Entity implements Attackable { this.hurt(this.damageSources().freeze(), 1.0F); } -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("push"); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("push"); if (this.autoSpinAttackTicks > 0) { --this.autoSpinAttackTicks; this.checkAutoSpinAttack(axisalignedbb, this.getBoundingBox()); } this.pushEntities(); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); // Paper start - if (((ServerLevel) this.level).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) { - if (this.xo != getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) { + if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) { + if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) { diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 02cb6b8c1d59855ff4a8aad3024fe12007eca0ee..a19956dbbb43f64043381bd968af74550b8a359a 100644 +index e2a25c29ec74147b3e66aa0b3deb85a8f6ee53a5..3bc50cc77bc8723dd607134f795aae55784aa657 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -144,8 +144,10 @@ public abstract class Mob extends LivingEntity implements Targeting { +@@ -143,8 +143,10 @@ public abstract class Mob extends LivingEntity implements Targeting { this.pathfindingMalus = Maps.newEnumMap(BlockPathTypes.class); this.restrictCenter = BlockPos.ZERO; this.restrictRadius = -1.0F; @@ -1177,77 +1205,79 @@ index 02cb6b8c1d59855ff4a8aad3024fe12007eca0ee..a19956dbbb43f64043381bd968af7455 this.lookControl = new LookControl(this); this.moveControl = new MoveControl(this); this.jumpControl = new JumpControl(this); -@@ -357,13 +359,10 @@ public abstract class Mob extends LivingEntity implements Targeting { +@@ -360,13 +362,10 @@ public abstract class Mob extends LivingEntity implements Targeting { @Override public void baseTick() { super.baseTick(); -- this.level.getProfiler().push("mobBaseTick"); +- this.level().getProfiler().push("mobBaseTick"); if (this.isAlive() && this.random.nextInt(1000) < this.ambientSoundTime++) { this.resetAmbientSoundTime(); this.playAmbientSound(); } - -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); } @Override -@@ -668,7 +667,6 @@ public abstract class Mob extends LivingEntity implements Targeting { +@@ -671,7 +670,6 @@ public abstract class Mob extends LivingEntity implements Targeting { @Override public void aiStep() { super.aiStep(); -- this.level.getProfiler().push("looting"); - if (!this.level.isClientSide && this.canPickUpLoot() && this.isAlive() && !this.dead && this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { +- this.level().getProfiler().push("looting"); + if (!this.level().isClientSide && this.canPickUpLoot() && this.isAlive() && !this.dead && this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { Vec3i baseblockposition = this.getPickupReach(); - List list = this.level.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate((double) baseblockposition.getX(), (double) baseblockposition.getY(), (double) baseblockposition.getZ())); -@@ -687,8 +685,6 @@ public abstract class Mob extends LivingEntity implements Targeting { + List list = this.level().getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate((double) baseblockposition.getX(), (double) baseblockposition.getY(), (double) baseblockposition.getZ())); +@@ -690,8 +688,6 @@ public abstract class Mob extends LivingEntity implements Targeting { } } } - -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); } protected Vec3i getPickupReach() { -@@ -900,42 +896,22 @@ public abstract class Mob extends LivingEntity implements Targeting { +@@ -902,43 +898,23 @@ public abstract class Mob extends LivingEntity implements Targeting { + } return; } - // Paper end -- this.level.getProfiler().push("sensing"); +- // Paper end +- this.level().getProfiler().push("sensing"); ++ // Paper end); this.sensing.tick(); -- this.level.getProfiler().pop(); - int i = this.level.getServer().getTickCount() + this.getId(); +- this.level().getProfiler().pop(); + int i = this.level().getServer().getTickCount() + this.getId(); if (i % 2 != 0 && this.tickCount > 1) { -- this.level.getProfiler().push("targetSelector"); +- this.level().getProfiler().push("targetSelector"); this.targetSelector.tickRunningGoals(false); -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("goalSelector"); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("goalSelector"); this.goalSelector.tickRunningGoals(false); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); } else { -- this.level.getProfiler().push("targetSelector"); +- this.level().getProfiler().push("targetSelector"); this.targetSelector.tick(); -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("goalSelector"); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("goalSelector"); this.goalSelector.tick(); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); } -- this.level.getProfiler().push("navigation"); +- this.level().getProfiler().push("navigation"); this.navigation.tick(); -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("mob tick"); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("mob tick"); this.customServerAiStep(); -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("controls"); -- this.level.getProfiler().push("move"); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("controls"); +- this.level().getProfiler().push("move"); this.moveControl.tick(); -- this.level.getProfiler().popPush("look"); +- this.level().getProfiler().popPush("look"); this.lookControl.tick(); -- this.level.getProfiler().popPush("jump"); +- this.level().getProfiler().popPush("jump"); this.jumpControl.tick(); -- this.level.getProfiler().pop(); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); +- this.level().getProfiler().pop(); this.sendDebugPackets(); } @@ -1325,7 +1355,7 @@ index b738ee2d3801fadfd09313f05ae24593e56b0ec6..ce2804271bb67803c60c9121aec6c8dc public Set getAvailableGoals() { diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java -index 7ffe5bef3778d5971ea4ceadf3102725fd0d08cd..ec797e7ab5185ae9f4ccae53023785ce780d89ad 100644 +index b376670d11088e524ce246f667e580e90cd119a3..7d7fbe9309f3957ecad5d47ba65cf8ebb66d3423 100644 --- a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java +++ b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java @@ -172,12 +172,10 @@ public abstract class PathNavigation { @@ -1342,224 +1372,224 @@ index 7ffe5bef3778d5971ea4ceadf3102725fd0d08cd..ec797e7ab5185ae9f4ccae53023785ce this.targetPos = path.getTarget(); this.reachRange = distance; diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java -index 288c6627906d07c0d223eacd84ae4eb31a349998..ed440b9a84ac0e4619c075491515fa072d6aebec 100644 +index 51772f03a3469b11e7166ec6f3a1b9c64a606221..ed440b9a84ac0e4619c075491515fa072d6aebec 100644 --- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java +++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java @@ -26,9 +26,7 @@ public class Sensing { } else if (this.unseen.contains(i)) { return false; } else { -- this.mob.level.getProfiler().push("hasLineOfSight"); +- this.mob.level().getProfiler().push("hasLineOfSight"); boolean bl = this.mob.hasLineOfSight(entity); -- this.mob.level.getProfiler().pop(); +- this.mob.level().getProfiler().pop(); if (bl) { this.seen.add(i); } else { diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -index 9b57d2b766f2de2d3fb4a3b5ef4df8d6756a1942..068c6904599f57b70b9cf166a5fe949362a7b23f 100644 +index 339c70f101d026a100a801e66cf514b3329a89d2..0014951b6e33ce72b4e0184946cf8bd6d6d2e5b0 100644 --- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java +++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -@@ -225,12 +225,8 @@ public class Allay extends PathfinderMob implements InventoryCarrier { +@@ -224,12 +224,8 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS @Override protected void customServerAiStep() { -- this.level.getProfiler().push("allayBrain"); - this.getBrain().tick((ServerLevel) this.level, this); -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("allayActivityUpdate"); +- this.level().getProfiler().push("allayBrain"); + this.getBrain().tick((ServerLevel) this.level(), this); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("allayActivityUpdate"); AllayAi.updateActivity(this); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); super.customServerAiStep(); } diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -index a4e98b02175da96472474b8d7ad5975ce4d2fc43..c513138be4b343ee1868a9ef541130a637fa0744 100644 +index 2682a49cd3948e0f80e2d7e58abcd3e6d8f7ac4e..634e884978094c48eaa8b1943ad0fb5cfc943f2c 100644 --- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java +++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java @@ -287,12 +287,8 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder optional = this.getBrain().getMemory(MemoryModuleType.PLAY_DEAD_TICKS); diff --git a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -index 05c7680569346bb863b896bcc9515f3e7cfb8114..8fd496e3d51fc55caff764981654347d33357004 100644 +index 4efa7e331cc974008c653a04687a336e97626445..b6e71424bff85c92dc17125614281c2e526ff7b6 100644 --- a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java +++ b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -@@ -149,13 +149,9 @@ public class Camel extends AbstractHorse implements PlayerRideableJumping, Rider +@@ -150,13 +150,9 @@ public class Camel extends AbstractHorse implements PlayerRideableJumping, Rider @Override protected void customServerAiStep() { -- this.level.getProfiler().push("camelBrain"); +- this.level().getProfiler().push("camelBrain"); Brain brain = (Brain) this.getBrain(); // Paper - decompile fix - brain.tick((ServerLevel)this.level, this); -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("camelActivityUpdate"); + brain.tick((ServerLevel)this.level(), this); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("camelActivityUpdate"); CamelAi.updateActivity(this); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); super.customServerAiStep(); } diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -index a65cec3c9837882df5b61de58f03d276d4db6bfc..c7b08018dbfb7d210ea4102518ea7e592ad01452 100644 +index 203691417e208b9e023e5f8c3b76993db2747ba8..62bf9294906c36c75367f660f9f2b9a03ebdbb86 100644 --- a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java +++ b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -@@ -169,12 +169,8 @@ public class Frog extends Animal implements VariantHolder { +@@ -164,12 +164,8 @@ public class Frog extends Animal implements VariantHolder { @Override protected void customServerAiStep() { -- this.level.getProfiler().push("frogBrain"); - this.getBrain().tick((ServerLevel)this.level, this); -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("frogActivityUpdate"); +- this.level().getProfiler().push("frogBrain"); + this.getBrain().tick((ServerLevel)this.level(), this); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("frogActivityUpdate"); FrogAi.updateActivity(this); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); super.customServerAiStep(); } diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -index 9058f9f2e561cda9f475f33218bf7a78297de4bc..09240841cd9216c06da8dc4059f8a60ef9022d39 100644 +index 4aeab90e778629c355189dfe79c39c4b21f5f5ac..0f3a11203dd0353d74626a273e9003131356f5e1 100644 --- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java @@ -79,12 +79,8 @@ public class Tadpole extends AbstractFish { @Override protected void customServerAiStep() { -- this.level.getProfiler().push("tadpoleBrain"); - this.getBrain().tick((ServerLevel) this.level, this); -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("tadpoleActivityUpdate"); +- this.level().getProfiler().push("tadpoleBrain"); + this.getBrain().tick((ServerLevel) this.level(), this); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("tadpoleActivityUpdate"); TadpoleAi.updateActivity(this); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); super.customServerAiStep(); } diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -index cfa904d42734d0fb0c1ed8b18f4d8bc131027962..ef584de97ba678176ab9bf61365d97ca61ff07cf 100644 +index 528e7ba29dcd38726b2c1bbc1d8ac208c64ba9df..61144e8c2ad1543816be16002b43622d1578ec73 100644 --- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java +++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java @@ -190,12 +190,8 @@ public class Goat extends Animal { @Override protected void customServerAiStep() { -- this.level.getProfiler().push("goatBrain"); - this.getBrain().tick((ServerLevel) this.level, this); -- this.level.getProfiler().pop(); -- this.level.getProfiler().push("goatActivityUpdate"); +- this.level().getProfiler().push("goatBrain"); + this.getBrain().tick((ServerLevel) this.level(), this); +- this.level().getProfiler().pop(); +- this.level().getProfiler().push("goatActivityUpdate"); GoatAi.updateActivity(this); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); super.customServerAiStep(); } diff --git a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java -index f6e2348b280eaefc0eb05bf5d962593caa654357..ab5ad2e225ba8640f1d37e190a7a710fdce1823c 100644 +index a6d98f64910c816a5c11867d12698f5cd63c751a..46ca9edde3fdfc5de5927ffc6d3282e79661e432 100644 --- a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java +++ b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java -@@ -443,11 +443,8 @@ public class Sniffer extends Animal { +@@ -492,11 +492,8 @@ public class Sniffer extends Animal { @Override protected void customServerAiStep() { -- this.level.getProfiler().push("snifferBrain"); - this.getBrain().tick((ServerLevel) this.level, this); -- this.level.getProfiler().popPush("snifferActivityUpdate"); +- this.level().getProfiler().push("snifferBrain"); + this.getBrain().tick((ServerLevel) this.level(), this); +- this.level().getProfiler().popPush("snifferActivityUpdate"); SnifferAi.updateActivity(this); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); super.customServerAiStep(); } diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -index 51ad507a3b625201ecca50bd92f8f089f3b4d60a..818d1f09b63ad0df9e3fae059a05801571a5606d 100644 +index b056e29671f0a8437387c990fc96a9f3c1d614a2..d0cd8aa0515a314490e8d80f00e04115905e75aa 100644 --- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java +++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java @@ -198,9 +198,7 @@ public class Zoglin extends Monster implements Enemy, HoglinBase { @Override protected void customServerAiStep() { -- this.level.getProfiler().push("zoglinBrain"); - this.getBrain().tick((ServerLevel)this.level, this); -- this.level.getProfiler().pop(); +- this.level().getProfiler().push("zoglinBrain"); + this.getBrain().tick((ServerLevel)this.level(), this); +- this.level().getProfiler().pop(); this.updateActivity(); } diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -index 5d3b3cb3a882eb5d716f678095a65b28d0967476..739f4f86af7951ea42a3b248b93989de8b8f4f54 100644 +index e8f6c34ea789136d63c0aa88aec90203ef6282b5..a4b9c36e4fa3f499493436219a1dfd18cda2162f 100644 --- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java +++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java @@ -128,9 +128,7 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { @Override protected void customServerAiStep() { -- this.level.getProfiler().push("hoglinBrain"); - this.getBrain().tick((ServerLevel)this.level, this); -- this.level.getProfiler().pop(); +- this.level().getProfiler().push("hoglinBrain"); + this.getBrain().tick((ServerLevel)this.level(), this); +- this.level().getProfiler().pop(); HoglinAi.updateActivity(this); if (this.isConverting()) { ++this.timeInOverworld; diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -index afa7ecfa8453da510ec5ccecb1ceeb1d9893d259..a2246ab400545284cb65c292012eaf8bb3376ad7 100644 +index 27d9145693a772cd1b5d171da303c934101f3be8..93dc4f2ac5a4302337de8ae3440a9fded2437c72 100644 --- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -@@ -310,9 +310,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento +@@ -307,9 +307,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento @Override protected void customServerAiStep() { -- this.level.getProfiler().push("piglinBrain"); - this.getBrain().tick((ServerLevel) this.level, this); -- this.level.getProfiler().pop(); +- this.level().getProfiler().push("piglinBrain"); + this.getBrain().tick((ServerLevel) this.level(), this); +- this.level().getProfiler().pop(); PiglinAi.updateActivity(this); super.customServerAiStep(); } diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -index e44eec821eaa1cd77569814000d4aa36253fb13d..edee6a47e67a9e02ac269dcc16b1932cedb4d8aa 100644 +index 5d1a30e09870a5a535c71a2e44b8e6933de0c1e8..5aab051998b67b7ba95cbf568de60e325b905eab 100644 --- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java @@ -85,9 +85,7 @@ public class PiglinBrute extends AbstractPiglin { @Override protected void customServerAiStep() { -- this.level.getProfiler().push("piglinBruteBrain"); - this.getBrain().tick((ServerLevel)this.level, this); -- this.level.getProfiler().pop(); +- this.level().getProfiler().push("piglinBruteBrain"); + this.getBrain().tick((ServerLevel)this.level(), this); +- this.level().getProfiler().pop(); PiglinBruteAi.updateActivity(this); PiglinBruteAi.maybePlayActivitySound(this); super.customServerAiStep(); diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -index b2b63d9df3c07696f47281e9be74f1799f50b93e..4544ce7e0f33b13a56cd4d4a3c905f71d370bbe6 100644 +index 97b763431bc5015448ee7a26a340635a932c950b..9ca38f97f5d0d533187cdcd549b1accebc93bc95 100644 --- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java +++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -@@ -274,9 +274,7 @@ public class Warden extends Monster implements VibrationListener.VibrationListen +@@ -275,9 +275,7 @@ public class Warden extends Monster implements VibrationSystem { protected void customServerAiStep() { - ServerLevel worldserver = (ServerLevel) this.level; + ServerLevel worldserver = (ServerLevel) this.level(); - worldserver.getProfiler().push("wardenBrain"); this.getBrain().tick(worldserver, this); -- this.level.getProfiler().pop(); +- this.level().getProfiler().pop(); super.customServerAiStep(); if ((this.tickCount + this.getId()) % 120 == 0) { Warden.applyDarknessAround(worldserver, this.position(), this, 20); diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 6023b9eb3001e1a98ab8b970d853c4e7c7603f4d..cf865df0763ebbe9ebbe74f32b0863e2636de63f 100644 +index e30d5ae3e2900f43d7cafde71b8196f26e872841..10749bebb1c94ec1a74bacdce3b9b7903d8c4322 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -246,9 +246,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - @Override - protected void customServerAiStep() { mobTick(false); } - protected void mobTick(boolean inactive) { -- this.level.getProfiler().push("villagerBrain"); - if (!inactive) this.getBrain().tick((ServerLevel) this.level, this); // Paper -- this.level.getProfiler().pop(); +@@ -252,9 +252,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + protected void customServerAiStep(final boolean inactive) { + // Paper end +- this.level().getProfiler().push("villagerBrain"); + if (!inactive) this.getBrain().tick((ServerLevel) this.level(), this); // Paper +- this.level().getProfiler().pop(); if (this.assignProfessionWhenSpawned) { this.assignProfessionWhenSpawned = false; } diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 185f7b1d4df59f5db7b85b529a2de6402630bf35..298689244d9d66d197b2b7728475084546219255 100644 +index 8f97c9df726ac20cfce7bdddd5dd4f8c5aa76c35..8f2c0dcb0ca69ea1b09bb055c6a9494980be8e92 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java @@ -382,7 +382,6 @@ public class Explosion { @@ -1579,7 +1609,7 @@ index 185f7b1d4df59f5db7b85b529a2de6402630bf35..298689244d9d66d197b2b77284750845 } diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 63122fb74a51547e23232a5409295369ee3aa353..262626a0a01ed05f3f49cebec8b31266b2c441e7 100644 +index 71189e98dcb71e625defe5a0bd6355597c9382e6..65258190f550a0a2653fc975430d8d88ff5ec259 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -35,7 +35,6 @@ import net.minecraft.sounds.SoundSource; @@ -1590,7 +1620,7 @@ index 63122fb74a51547e23232a5409295369ee3aa353..262626a0a01ed05f3f49cebec8b31266 import net.minecraft.world.DifficultyInstance; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageSources; -@@ -130,7 +129,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -129,7 +128,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { private final ResourceKey dimensionTypeId; private final Holder dimensionTypeRegistration; public final WritableLevelData levelData; @@ -1598,7 +1628,7 @@ index 63122fb74a51547e23232a5409295369ee3aa353..262626a0a01ed05f3f49cebec8b31266 public final boolean isClientSide; private final WorldBorder worldBorder; private final BiomeManager biomeManager; -@@ -272,7 +270,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -271,7 +269,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public abstract ResourceKey getTypeKey(); @@ -1607,7 +1637,7 @@ index 63122fb74a51547e23232a5409295369ee3aa353..262626a0a01ed05f3f49cebec8b31266 this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper this.galeConfig = galeWorldConfigCreator.apply(this.spigotConfig); // Gale - Gale configuration -@@ -287,7 +285,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -286,7 +284,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { } // CraftBukkit end @@ -1615,17 +1645,7 @@ index 63122fb74a51547e23232a5409295369ee3aa353..262626a0a01ed05f3f49cebec8b31266 this.levelData = worlddatamutable; this.dimensionTypeRegistration = holder; this.dimensionTypeId = (ResourceKey) holder.unwrapKey().orElseThrow(() -> { -@@ -568,9 +565,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - BlockState iblockdata2 = this.getBlockState(pos); - - if ((flags & 128) == 0 && iblockdata2 != iblockdata1 && (iblockdata2.getLightBlock(this, pos) != iblockdata1.getLightBlock(this, pos) || iblockdata2.getLightEmission() != iblockdata1.getLightEmission() || iblockdata2.useShapeForLightOcclusion() || iblockdata1.useShapeForLightOcclusion())) { -- this.getProfiler().push("queueCheckLight"); - this.getChunkSource().getLightEngine().checkBlock(pos); -- this.getProfiler().pop(); - } - - /* -@@ -869,9 +864,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -862,9 +859,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { } protected void tickBlockEntities() { @@ -1635,7 +1655,7 @@ index 63122fb74a51547e23232a5409295369ee3aa353..262626a0a01ed05f3f49cebec8b31266 timings.tileEntityPending.startTiming(); // Spigot this.tickingBlockEntities = true; if (!this.pendingBlockEntityTickers.isEmpty()) { -@@ -916,7 +908,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -909,7 +903,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { timings.tileEntityTick.stopTiming(); // Spigot this.tickingBlockEntities = false; co.aikar.timings.TimingHistory.tileEntityTicks += this.blockEntityTickers.size(); // Paper @@ -1643,7 +1663,7 @@ index 63122fb74a51547e23232a5409295369ee3aa353..262626a0a01ed05f3f49cebec8b31266 spigotConfig.currentPrimedTnt = 0; // Spigot } -@@ -1109,7 +1100,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1102,7 +1095,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { @Override public List getEntities(@Nullable Entity except, AABB box, Predicate predicate) { @@ -1651,7 +1671,7 @@ index 63122fb74a51547e23232a5409295369ee3aa353..262626a0a01ed05f3f49cebec8b31266 List list = Lists.newArrayList(); ((ServerLevel)this).getEntityLookup().getEntities(except, box, list, predicate); // Paper - optimise this call return list; -@@ -1128,7 +1118,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1121,7 +1113,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { } public void getEntities(EntityTypeTest filter, AABB box, Predicate predicate, List result, int limit) { @@ -1659,7 +1679,7 @@ index 63122fb74a51547e23232a5409295369ee3aa353..262626a0a01ed05f3f49cebec8b31266 // Paper start - optimise this call //TODO use limit if (filter instanceof net.minecraft.world.entity.EntityType entityTypeTest) { -@@ -1452,14 +1441,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1377,14 +1368,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable { return false; } @@ -1675,7 +1695,7 @@ index 63122fb74a51547e23232a5409295369ee3aa353..262626a0a01ed05f3f49cebec8b31266 public BiomeManager getBiomeManager() { return this.biomeManager; diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index 15d266fc97eb73338f4f6fb2cfe25d6861e79810..eebc251467ddc141b2ae0408941740f6b01d11f2 100644 +index 089dd93d4cd4c1f72e63c4944b3b82c1e2ba732d..599efa2ed24e0d4944324c86ca23c38d8b111277 100644 --- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java +++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java @@ -132,7 +132,6 @@ public final class NaturalSpawner { @@ -1708,18 +1728,32 @@ index 0f1025495237aebe30132ace0832aa5718d6f9bb..efe922810507c96183a56a5e81a7b142 - } } diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index d190bad5d287766ed4165ed827d9901a9d878687..6ba3d1721a781522df24b255beee6dc61b9144ae 100644 +index 4ff0d2fc9fd76e92e64abd69f2c9e299aa08ac32..624c96b0907dfb8b6c351710bed58df78690a483 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -28,7 +28,6 @@ import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; - import net.minecraft.server.level.ChunkHolder; - import net.minecraft.server.level.ServerChunkCache; +@@ -25,7 +25,6 @@ import net.minecraft.network.FriendlyByteBuf; + import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; + import net.minecraft.server.level.FullChunkStatus; import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; -@@ -1301,9 +1300,6 @@ public class LevelChunk extends ChunkAccess { +@@ -536,13 +535,8 @@ public class LevelChunk extends ChunkAccess { + } + + if (LightEngine.hasDifferentLightProperties(this, blockposition, iblockdata1, iblockdata)) { +- ProfilerFiller gameprofilerfiller = this.level.getProfiler(); +- +- gameprofilerfiller.push("updateSkyLightSources"); + this.skyLightSources.update(this, j, i, l); +- gameprofilerfiller.popPush("queueCheckLight"); + this.level.getChunkSource().getLightEngine().checkBlock(blockposition); +- gameprofilerfiller.pop(); + } + + boolean flag3 = iblockdata1.hasBlockEntity(); +@@ -1256,9 +1250,6 @@ public class LevelChunk extends ChunkAccess { if (LevelChunk.this.isTicking(blockposition)) { try { @@ -1729,7 +1763,7 @@ index d190bad5d287766ed4165ed827d9901a9d878687..6ba3d1721a781522df24b255beee6dc6 this.blockEntity.tickTimer.startTiming(); // Spigot BlockState iblockdata = LevelChunk.this.getBlockState(blockposition); -@@ -1314,8 +1310,6 @@ public class LevelChunk extends ChunkAccess { +@@ -1269,8 +1260,6 @@ public class LevelChunk extends ChunkAccess { this.loggedInvalidBlockState = true; LevelChunk.LOGGER.warn("Block entity {} @ {} state {} invalid for ticking:", new Object[]{LogUtils.defer(this::getType), LogUtils.defer(this::getPos), iblockdata}); } @@ -1770,6 +1804,27 @@ index d23481453717f715124156b5d83f6448f720d049..c4052d1a7c2903564a8a6226c1b019d2 // Set set = positions.keySet(); startNode.g = 0.0F; startNode.h = this.getBestH(startNode, positions); // Paper - optimize collection +diff --git a/src/main/java/net/minecraft/world/level/storage/loot/LootDataManager.java b/src/main/java/net/minecraft/world/level/storage/loot/LootDataManager.java +index 3cce32f308a9f546cae7a9140e33d97d766d7d70..32c651ebbbb798345429c561fd6ffe688a4b069f 100644 +--- a/src/main/java/net/minecraft/world/level/storage/loot/LootDataManager.java ++++ b/src/main/java/net/minecraft/world/level/storage/loot/LootDataManager.java +@@ -19,7 +19,6 @@ import net.minecraft.resources.ResourceLocation; + import net.minecraft.server.packs.resources.PreparableReloadListener; + import net.minecraft.server.packs.resources.ResourceManager; + import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; +-import net.minecraft.util.profiling.ProfilerFiller; + import net.minecraft.world.item.ItemStack; + import net.minecraft.world.level.storage.loot.functions.LootItemFunction; + import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType; +@@ -41,7 +40,7 @@ public class LootDataManager implements PreparableReloadListener, LootDataResolv + public LootDataManager() {} + + @Override +- public final CompletableFuture reload(PreparableReloadListener.PreparationBarrier synchronizer, ResourceManager manager, ProfilerFiller prepareProfiler, ProfilerFiller applyProfiler, Executor prepareExecutor, Executor applyExecutor) { ++ public final CompletableFuture reload(PreparableReloadListener.PreparationBarrier synchronizer, ResourceManager manager, Executor prepareExecutor, Executor applyExecutor) { // Gale - Purpur - remove vanilla profiler + Map, Map> map = new HashMap(); + CompletableFuture[] acompletablefuture = (CompletableFuture[]) LootDataType.values().map((lootdatatype) -> { + return LootDataManager.scheduleElementParse(lootdatatype, manager, prepareExecutor, map); diff --git a/src/main/java/net/minecraft/world/ticks/LevelTicks.java b/src/main/java/net/minecraft/world/ticks/LevelTicks.java index 1d7c663fa0e550bd0cfb9a4b83ccd7e2968666f0..cf685940e09251bef1a3d06f1e2468ce4ff46cc5 100644 --- a/src/main/java/net/minecraft/world/ticks/LevelTicks.java diff --git a/patches/server/0018-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch b/patches/server/0018-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch index 5102206..24a48c9 100644 --- a/patches/server/0018-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch +++ b/patches/server/0018-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch @@ -40,23 +40,23 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index b4b916f2929d83aaa160a69a198836ed53430e23..517a4cde947b51195ca7e99afc330d8b40fae235 100644 +index 5cf43c4d0ce6e3af5ea6c190db0c47c35b0a350c..59db3933d1abb161b7ea6b9164653e96255cb545 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -3591,7 +3591,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3640,7 +3640,7 @@ public abstract class LivingEntity extends Entity implements Attackable { Vec3 vec3d1 = new Vec3(entity.getX(), entity.getEyeY(), entity.getZ()); // Paper - diff on change - used in CraftLivingEntity#hasLineOfSight(Location) and CraftWorld#lineOfSightExists -- return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level.clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; // Paper - use distanceToSqr -+ return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level.rayTraceDirect(vec3d, vec3d1, net.minecraft.world.phys.shapes.CollisionContext.of(this)) == net.minecraft.world.phys.BlockHitResult.Type.MISS; // Paper - use distanceToSqr // Gale - Airplane - strip raytracing for EntityLiving#hasLineOfSight +- return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level().clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; ++ return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level().rayTraceDirect(vec3d, vec3d1, net.minecraft.world.phys.shapes.CollisionContext.of(this)) == net.minecraft.world.phys.BlockHitResult.Type.MISS; // Paper - use distanceToSqr // Gale - Airplane - strip raytracing for EntityLiving#hasLineOfSight } } diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java -index 2ee9e8e3c1a28c1823de8e1fe421cc1f3e72f384..9d41fc2ed4fa623d5fac62fbb3340d13c78a746d 100644 +index 0e8746759752b692668886370181aa5db1fd0bb0..e5e50817c83b6a7d3daf90960ed402180998ca49 100644 --- a/src/main/java/net/minecraft/world/level/BlockGetter.java +++ b/src/main/java/net/minecraft/world/level/BlockGetter.java -@@ -73,6 +73,16 @@ public interface BlockGetter extends LevelHeightAccessor { +@@ -68,6 +68,16 @@ public interface BlockGetter extends LevelHeightAccessor { }); } @@ -74,10 +74,10 @@ index 2ee9e8e3c1a28c1823de8e1fe421cc1f3e72f384..9d41fc2ed4fa623d5fac62fbb3340d13 default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) { // Paper start - Prevent raytrace from loading chunks diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 262626a0a01ed05f3f49cebec8b31266b2c441e7..c512c37439fa2b40bbd1d40301686bd13fa0469e 100644 +index 65258190f550a0a2653fc975430d8d88ff5ec259..0502f5bf4ee886ddc16f430a4c6679279cc12ff1 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -409,6 +409,91 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -408,6 +408,91 @@ public abstract class Level implements LevelAccessor, AutoCloseable { return null; } diff --git a/patches/server/0020-Reduce-projectile-chunk-loading.patch b/patches/server/0020-Reduce-projectile-chunk-loading.patch index 85dcbfc..469b88c 100644 --- a/patches/server/0020-Reduce-projectile-chunk-loading.patch +++ b/patches/server/0020-Reduce-projectile-chunk-loading.patch @@ -31,10 +31,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -index 8b2a3a8482018b7db7de81bc295862f783e17ce5..64f6555372ba5c97891fd6742f51fa6680c690a2 100644 +index 1b7cf6d06bdf36f146656727511a461f2520762e..d598a99072bd8eb4a4ed151b9202237dd0c2af8c 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -@@ -44,6 +44,44 @@ public abstract class Projectile extends Entity implements TraceableEntity { +@@ -44,6 +44,45 @@ public abstract class Projectile extends Entity implements TraceableEntity { super(type, world); } @@ -53,17 +53,18 @@ index 8b2a3a8482018b7db7de81bc295862f783e17ce5..64f6555372ba5c97891fd6742f51fa66 + int previousX = Mth.floor(this.getX()) >> 4, previousZ = Mth.floor(this.getZ()) >> 4; + int newX = Mth.floor(x) >> 4, newZ = Mth.floor(z) >> 4; + if (previousX != newX || previousZ != newZ) { -+ boolean isLoaded = ((net.minecraft.server.level.ServerChunkCache) this.level.getChunkSource()).getChunkAtIfLoadedMainThread(newX, newZ) != null; ++ boolean isLoaded = ((net.minecraft.server.level.ServerChunkCache) this.level().getChunkSource()).getChunkAtIfLoadedMainThread(newX, newZ) != null; + if (!isLoaded) { -+ int maxChunkLoadsPerTick = this.level.galeConfig().smallOptimizations.maxProjectileChunkLoads.perTick; ++ var maxProjectileChunkLoadsConfig = this.level().galeConfig().smallOptimizations.maxProjectileChunkLoads; ++ int maxChunkLoadsPerTick = maxProjectileChunkLoadsConfig.perTick; + if (maxChunkLoadsPerTick >= 0 && chunksLoadedThisTick > maxChunkLoadsPerTick) { + return; + } -+ int maxChunkLoadsPerProjectile = this.level.galeConfig().smallOptimizations.maxProjectileChunkLoads.perProjectile.max; ++ int maxChunkLoadsPerProjectile = maxProjectileChunkLoadsConfig.perProjectile.max; + if (maxChunkLoadsPerProjectile >= 0 && this.chunksLoadedByProjectile >= maxChunkLoadsPerProjectile) { -+ if (this.level.galeConfig().smallOptimizations.maxProjectileChunkLoads.perProjectile.removeFromWorldAfterReachLimit) { ++ if (maxProjectileChunkLoadsConfig.perProjectile.removeFromWorldAfterReachLimit) { + this.discard(); -+ } else if (this.level.galeConfig().smallOptimizations.maxProjectileChunkLoads.perProjectile.resetMovementAfterReachLimit) { ++ } else if (maxProjectileChunkLoadsConfig.perProjectile.resetMovementAfterReachLimit) { + this.setDeltaMovement(0, this.getDeltaMovement().y, 0); + } + return; diff --git a/patches/server/0022-Move-random-tick-random.patch b/patches/server/0022-Move-random-tick-random.patch index 012bc22..a43557b 100644 --- a/patches/server/0022-Move-random-tick-random.patch +++ b/patches/server/0022-Move-random-tick-random.patch @@ -19,10 +19,10 @@ require it to be initialized earlier. By moving it to the superclass, we initialize it earlier, ensuring that it is available sooner. diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 42b0aa27af33aa5879f9d9a5fcfa19c296fbc635..c0424d758bf2921ae89cfdf89ff71980684e5d0c 100644 +index 287f6a38a6f042983c5e652ff9800fb215a91221..47acb63bfca2f722a595cf6614c28e75d1d94235 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -770,7 +770,6 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -911,7 +911,6 @@ public class ServerLevel extends Level implements WorldGenLevel { } // Paper start - optimise random block ticking private final BlockPos.MutableBlockPos chunkTickMutablePosition = new BlockPos.MutableBlockPos(); @@ -31,10 +31,10 @@ index 42b0aa27af33aa5879f9d9a5fcfa19c296fbc635..c0424d758bf2921ae89cfdf89ff71980 public void tickChunk(LevelChunk chunk, int randomTickSpeed) { diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index c512c37439fa2b40bbd1d40301686bd13fa0469e..67de286dd6a569da9f8b36a4c7bd673830c17a2b 100644 +index 0502f5bf4ee886ddc16f430a4c6679279cc12ff1..5185af09005fd9f671daca0844b42e85b29a0376 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -181,6 +181,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -180,6 +180,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public final Map explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Move from Map in BlockRedstoneTorch to here diff --git a/patches/server/0023-Optimize-random-calls-in-chunk-ticking.patch b/patches/server/0023-Optimize-random-calls-in-chunk-ticking.patch index 4c6851c..f2a7ebb 100644 --- a/patches/server/0023-Optimize-random-calls-in-chunk-ticking.patch +++ b/patches/server/0023-Optimize-random-calls-in-chunk-ticking.patch @@ -52,10 +52,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index d95c3b14c06421c03c99f84efb3a5319200f8a57..8ed18a158a13dceca7cd342125c09e52ac0d013c 100644 +index 78201998d6cbee9aa6f0bad80b726b46675393dc..041f188b742ad819e2460e8ca5ab7f44412dbf9b 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -689,6 +689,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -539,6 +539,7 @@ public class ServerChunkCache extends ChunkSource { // Paper end - optimize isOutisdeRange LevelData worlddata = this.level.getLevelData(); @@ -64,10 +64,10 @@ index d95c3b14c06421c03c99f84efb3a5319200f8a57..8ed18a158a13dceca7cd342125c09e52 boolean flag1 = level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && worlddata.getGameTime() % level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index f9e58d0b2d4b41bbfadf2ab9591914e7090c61dd..cb0df7b5db3fa885f43b35e6ef8c6cdc221b4003 100644 +index 47acb63bfca2f722a595cf6614c28e75d1d94235..b7318670e039e05a058e437e025519d40557bb88 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -772,6 +772,8 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -913,6 +913,8 @@ public class ServerLevel extends Level implements WorldGenLevel { private final BlockPos.MutableBlockPos chunkTickMutablePosition = new BlockPos.MutableBlockPos(); // Paper end @@ -76,7 +76,7 @@ index f9e58d0b2d4b41bbfadf2ab9591914e7090c61dd..cb0df7b5db3fa885f43b35e6ef8c6cdc public void tickChunk(LevelChunk chunk, int randomTickSpeed) { ChunkPos chunkcoordintpair = chunk.getPos(); boolean flag = this.isRaining(); -@@ -779,7 +781,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -920,7 +922,7 @@ public class ServerLevel extends Level implements WorldGenLevel { int k = chunkcoordintpair.getMinBlockZ(); final BlockPos.MutableBlockPos blockposition = this.chunkTickMutablePosition; // Paper - use mutable to reduce allocation rate, final to force compile fail on change @@ -85,9 +85,9 @@ index f9e58d0b2d4b41bbfadf2ab9591914e7090c61dd..cb0df7b5db3fa885f43b35e6ef8c6cdc blockposition.set(this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15))); // Paper if (this.isRainingAt(blockposition)) { DifficultyInstance difficultydamagescaler = this.getCurrentDifficultyAt(blockposition); -@@ -808,7 +810,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - +@@ -950,7 +952,7 @@ public class ServerLevel extends Level implements WorldGenLevel { int l; + int i1; - if (!this.paperConfig().environment.disableIceAndSnow && this.random.nextInt(16) == 0) { // Paper - Disable ice and snow + if (!this.paperConfig().environment.disableIceAndSnow && (this.currentIceAndSnowTick++ & 15) == 0) { // Paper - Disable ice and snow // Paper - optimise random ticking // Gale - Airplane - optimize random calls in chunk ticking - optimize further random ticking @@ -95,10 +95,10 @@ index f9e58d0b2d4b41bbfadf2ab9591914e7090c61dd..cb0df7b5db3fa885f43b35e6ef8c6cdc this.getRandomBlockPosition(j, 0, k, 15, blockposition); int normalY = chunk.getHeight(Heightmap.Types.MOTION_BLOCKING, blockposition.getX() & 15, blockposition.getZ() & 15) + 1; diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 6ba3d1721a781522df24b255beee6dc61b9144ae..18c3f13c523ea62a098f489636b4320c73da4b8b 100644 +index 624c96b0907dfb8b6c351710bed58df78690a483..901938201c1abee1c88e217d2c1ba1a2d147420e 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -87,6 +87,18 @@ public class LevelChunk extends ChunkAccess { +@@ -84,6 +84,18 @@ public class LevelChunk extends ChunkAccess { private final LevelChunkTicks blockTicks; private final LevelChunkTicks fluidTicks; @@ -117,7 +117,7 @@ index 6ba3d1721a781522df24b255beee6dc61b9144ae..18c3f13c523ea62a098f489636b4320c public LevelChunk(Level world, ChunkPos pos) { this(world, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, (LevelChunkSection[]) null, (LevelChunk.PostLoadProcessor) null, (BlendingData) null); } -@@ -115,6 +127,7 @@ public class LevelChunk extends ChunkAccess { +@@ -111,6 +123,7 @@ public class LevelChunk extends ChunkAccess { this.postLoad = entityLoader; this.blockTicks = blockTickScheduler; this.fluidTicks = fluidTickScheduler; diff --git a/patches/server/0024-Reduce-enderman-teleport-chunk-lookups.patch b/patches/server/0024-Reduce-enderman-teleport-chunk-lookups.patch index 95168b8..ae26f75 100644 --- a/patches/server/0024-Reduce-enderman-teleport-chunk-lookups.patch +++ b/patches/server/0024-Reduce-enderman-teleport-chunk-lookups.patch @@ -31,26 +31,26 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index 418d6301f067803e2471e59ac2d52a68cbff249b..079bd9d0d3c73da61297723aea8e79edf531004b 100644 +index 4616addb05fcc20b1ec6ca1dea01cbefed697acd..82e8ddfd136d04106c796d784028c45e5c21dedd 100644 --- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java @@ -322,11 +322,17 @@ public class EnderMan extends Monster implements NeutralMob { private boolean teleport(double x, double y, double z) { BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(x, y, z); -- while (blockposition_mutableblockposition.getY() > this.level.getMinBuildHeight() && !this.level.getBlockState(blockposition_mutableblockposition).getMaterial().blocksMotion()) { +- while (blockposition_mutableblockposition.getY() > this.level().getMinBuildHeight() && !this.level().getBlockState(blockposition_mutableblockposition).blocksMotion()) { + // Gale start - Airplane - single chunk lookup -+ net.minecraft.world.level.chunk.LevelChunk chunk = this.level.getChunkIfLoaded(blockposition_mutableblockposition); ++ net.minecraft.world.level.chunk.LevelChunk chunk = this.level().getChunkIfLoaded(blockposition_mutableblockposition); + if (chunk == null) { + return false; + } -+ while (blockposition_mutableblockposition.getY() > this.level.getMinBuildHeight() && !chunk.getBlockState(blockposition_mutableblockposition).getMaterial().blocksMotion()) { ++ while (blockposition_mutableblockposition.getY() > this.level().getMinBuildHeight() && !chunk.getBlockState(blockposition_mutableblockposition).blocksMotion()) { + // Gale end - Airplane - single chunk lookup blockposition_mutableblockposition.move(Direction.DOWN); } -- BlockState iblockdata = this.level.getBlockState(blockposition_mutableblockposition); +- BlockState iblockdata = this.level().getBlockState(blockposition_mutableblockposition); + BlockState iblockdata = chunk.getBlockState(blockposition_mutableblockposition); // Gale - Airplane - single chunk lookup - boolean flag = iblockdata.getMaterial().blocksMotion(); + boolean flag = iblockdata.blocksMotion(); boolean flag1 = iblockdata.getFluidState().is(FluidTags.WATER); diff --git a/patches/server/0026-Remove-iterators-from-Inventory-contains.patch b/patches/server/0026-Remove-iterators-from-Inventory-contains.patch index 2a84de7..7e7b6f1 100644 --- a/patches/server/0026-Remove-iterators-from-Inventory-contains.patch +++ b/patches/server/0026-Remove-iterators-from-Inventory-contains.patch @@ -31,10 +31,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/world/entity/player/Inventory.java b/src/main/java/net/minecraft/world/entity/player/Inventory.java -index 27c028ab6b1edb6e413af3bbaa27bf30f2d85540..5e0e5a59fced422b931009f98550fd0f8729d9f4 100644 +index 395cecc385e4126a7534ac9aeb15cf323efab03e..cbcbb826733cf75bb018752af75a6a338947c472 100644 --- a/src/main/java/net/minecraft/world/entity/player/Inventory.java +++ b/src/main/java/net/minecraft/world/entity/player/Inventory.java -@@ -682,6 +682,8 @@ public class Inventory implements Container, Nameable { +@@ -681,6 +681,8 @@ public class Inventory implements Container, Nameable { } public boolean contains(ItemStack stack) { @@ -43,7 +43,7 @@ index 27c028ab6b1edb6e413af3bbaa27bf30f2d85540..5e0e5a59fced422b931009f98550fd0f Iterator iterator = this.compartments.iterator(); while (iterator.hasNext()) { -@@ -696,7 +698,18 @@ public class Inventory implements Container, Nameable { +@@ -695,7 +697,18 @@ public class Inventory implements Container, Nameable { } } } @@ -53,7 +53,7 @@ index 27c028ab6b1edb6e413af3bbaa27bf30f2d85540..5e0e5a59fced422b931009f98550fd0f + for (int j = 0; j < list.size(); j++) { + ItemStack itemstack1 = list.get(j); -+ if (!itemstack1.isEmpty() && itemstack1.sameItem(stack)) { ++ if (!itemstack1.isEmpty() && ItemStack.isSameItem(itemstack1, stack)) { + return true; + } + } diff --git a/patches/server/0029-Improve-fluid-direction-caching.patch b/patches/server/0029-Improve-fluid-direction-caching.patch index 006bf33..22417aa 100644 --- a/patches/server/0029-Improve-fluid-direction-caching.patch +++ b/patches/server/0029-Improve-fluid-direction-caching.patch @@ -186,10 +186,10 @@ index 0000000000000000000000000000000000000000..bf6a36c933b686f0e34591dff3d43b37 + } +} diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -index bf4de7b8fd630c596e096a411a8c84c64c13ebf7..363aed175ecc5cccd8f798503841948b72a98d5b 100644 +index 5502ad143fd2575f1346334b5b4fe7846628f54e..974cd5ff0836d89127be190b1335d945df1cb73c 100644 --- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -@@ -43,6 +43,8 @@ public abstract class FlowingFluid extends Fluid { +@@ -44,6 +44,8 @@ public abstract class FlowingFluid extends Fluid { public static final BooleanProperty FALLING = BlockStateProperties.FALLING; public static final IntegerProperty LEVEL = BlockStateProperties.LEVEL_FLOWING; private static final int CACHE_SIZE = 200; @@ -198,7 +198,7 @@ index bf4de7b8fd630c596e096a411a8c84c64c13ebf7..363aed175ecc5cccd8f798503841948b private static final ThreadLocal> OCCLUSION_CACHE = ThreadLocal.withInitial(() -> { Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap = new Object2ByteLinkedOpenHashMap(200) { protected void rehash(int i) {} -@@ -51,6 +53,14 @@ public abstract class FlowingFluid extends Fluid { +@@ -52,6 +54,14 @@ public abstract class FlowingFluid extends Fluid { object2bytelinkedopenhashmap.defaultReturnValue((byte) 127); return object2bytelinkedopenhashmap; }); @@ -213,7 +213,7 @@ index bf4de7b8fd630c596e096a411a8c84c64c13ebf7..363aed175ecc5cccd8f798503841948b private final Map shapes = Maps.newIdentityHashMap(); public FlowingFluid() {} -@@ -239,6 +249,8 @@ public abstract class FlowingFluid extends Fluid { +@@ -240,6 +250,8 @@ public abstract class FlowingFluid extends Fluid { } private boolean canPassThroughWall(Direction face, BlockGetter world, BlockPos pos, BlockState state, BlockPos fromPos, BlockState fromState) { @@ -222,7 +222,7 @@ index bf4de7b8fd630c596e096a411a8c84c64c13ebf7..363aed175ecc5cccd8f798503841948b Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap; if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) { -@@ -246,9 +258,16 @@ public abstract class FlowingFluid extends Fluid { +@@ -247,9 +259,16 @@ public abstract class FlowingFluid extends Fluid { } else { object2bytelinkedopenhashmap = null; } @@ -239,7 +239,7 @@ index bf4de7b8fd630c596e096a411a8c84c64c13ebf7..363aed175ecc5cccd8f798503841948b if (object2bytelinkedopenhashmap != null) { block_a = new Block.BlockStatePairKey(state, fromState, face); byte b0 = object2bytelinkedopenhashmap.getAndMoveToFirst(block_a); -@@ -259,11 +278,22 @@ public abstract class FlowingFluid extends Fluid { +@@ -260,11 +279,22 @@ public abstract class FlowingFluid extends Fluid { } else { block_a = null; } @@ -262,7 +262,7 @@ index bf4de7b8fd630c596e096a411a8c84c64c13ebf7..363aed175ecc5cccd8f798503841948b if (object2bytelinkedopenhashmap != null) { if (object2bytelinkedopenhashmap.size() == 200) { object2bytelinkedopenhashmap.removeLastByte(); -@@ -271,6 +301,11 @@ public abstract class FlowingFluid extends Fluid { +@@ -272,6 +302,11 @@ public abstract class FlowingFluid extends Fluid { object2bytelinkedopenhashmap.putAndMoveToFirst(block_a, (byte) (flag ? 1 : 0)); } diff --git a/patches/server/0030-Cache-on-climbable-check.patch b/patches/server/0030-Cache-on-climbable-check.patch index 981e35a..8514c41 100644 --- a/patches/server/0030-Cache-on-climbable-check.patch +++ b/patches/server/0030-Cache-on-climbable-check.patch @@ -31,10 +31,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 8d90c02c8f0b82e9c31889b35dcfbe2932406b1d..6411a059a67e1a09580ec8b11afbffe1ef4b8958 100644 +index 66b562e3f16050ebee50b8b440b48c4e3c7049d3..f86cb887d2cc3ae38c8d38675c3ac5ff2992242d 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -141,7 +141,6 @@ import org.bukkit.event.entity.EntityTeleportEvent; +@@ -143,7 +143,6 @@ import org.bukkit.event.entity.EntityTeleportEvent; import org.bukkit.event.player.PlayerItemConsumeEvent; // CraftBukkit end @@ -42,7 +42,7 @@ index 8d90c02c8f0b82e9c31889b35dcfbe2932406b1d..6411a059a67e1a09580ec8b11afbffe1 public abstract class LivingEntity extends Entity implements Attackable { -@@ -1927,6 +1926,20 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1970,6 +1969,20 @@ public abstract class LivingEntity extends Entity implements Attackable { return this.lastClimbablePos; } @@ -64,7 +64,7 @@ index 8d90c02c8f0b82e9c31889b35dcfbe2932406b1d..6411a059a67e1a09580ec8b11afbffe1 if (this.isSpectator()) { return false; diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index 52780192d6417f8085566e4cdf3a895a83638520..a04c202e83abc08a98549440e987d4d5f47379b7 100644 +index d4da9ec6e00bb92b70598ee9a0d0ca5816562378..713f62b7bfe290b1eb0eda6e3acc1d3583029aa6 100644 --- a/src/main/java/org/spigotmc/ActivationRange.java +++ b/src/main/java/org/spigotmc/ActivationRange.java @@ -292,7 +292,7 @@ public class ActivationRange diff --git a/patches/server/0032-Improve-container-checking-with-a-bitset.patch b/patches/server/0032-Improve-container-checking-with-a-bitset.patch index af732c3..51393c4 100644 --- a/patches/server/0032-Improve-container-checking-with-a-bitset.patch +++ b/patches/server/0032-Improve-container-checking-with-a-bitset.patch @@ -265,7 +265,7 @@ index 04b1531572e8fff1e46fe1c94e7fc863841e0f66..0d22bce549c46d686ab3402a1cad8730 int LARGE_MAX_STACK_SIZE = 64; int DEFAULT_DISTANCE_LIMIT = 8; diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java -index 08f027cdcaeeca7b545483cb8c5eb8d13e4933b9..ef02bd4948c8c6bcf1d3da1a7e1c94d6628cd505 100644 +index 272095d7a09ab41227d741172735f66fd2798ce1..77eee9c231ea075feb1ea0492d0be56554f78ec8 100644 --- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java +++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java @@ -27,7 +27,10 @@ import org.bukkit.inventory.InventoryHolder; @@ -390,7 +390,7 @@ index a71414397bd45ee7bcacfeef0041d80dfa25f114..7743f185672e977520218c3ce6b8ddc9 @Override diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java -index aac5572c1d40a10cd1d17f89c9eb836718837577..1ad1716d27e58db05c1e22f7f2781319debde925 100644 +index 93c13cb36024370f6ec71137cebca254af2b3b61..9849e784f4fc92368779134f8e10fa2f290a086b 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java @@ -47,7 +47,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen @@ -498,10 +498,10 @@ index aac5572c1d40a10cd1d17f89c9eb836718837577..1ad1716d27e58db05c1e22f7f2781319 public static void entityInside(Level world, BlockPos pos, BlockState state, Entity entity, HopperBlockEntity blockEntity) { diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -index 79b01e32f89defb6b78f4764600d33d4945af592..a05f7c8815d564bf1b95b6ce35ca2ac71ec84c9f 100644 +index 081691f9710ff1115e4308f79ed49fbc38941193..6347459c1e7347914642c23fe919526db879d103 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -@@ -97,12 +97,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc +@@ -96,12 +96,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc public boolean isEmpty() { this.unpackLootTable((Player)null); // Paper start diff --git a/patches/server/0033-Better-checking-for-useless-move-packets.patch b/patches/server/0033-Better-checking-for-useless-move-packets.patch index 1cda9e5..b2936ee 100644 --- a/patches/server/0033-Better-checking-for-useless-move-packets.patch +++ b/patches/server/0033-Better-checking-for-useless-move-packets.patch @@ -31,22 +31,22 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index b7fd8e70413c38923d0719aff803449e392383ac..48adbfbb8a55f6719c92a1fe83c64d87f1b236d5 100644 +index 6670e657e08e130f7e0368f418379fd1ece00cdf..1717844256fe6479e3d7125db3937354578d17d0 100644 --- a/src/main/java/net/minecraft/server/level/ServerEntity.java +++ b/src/main/java/net/minecraft/server/level/ServerEntity.java -@@ -185,6 +185,7 @@ public class ServerEntity { +@@ -181,6 +181,7 @@ public class ServerEntity { boolean flag6 = k < -32768L || k > 32767L || l < -32768L || l > 32767L || i1 < -32768L || i1 > 32767L; - if (!flag6 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.isOnGround() && !(io.papermc.paper.configuration.GlobalConfiguration.get().collisions.sendFullPosForHardCollidingEntities && this.entity.hardCollides())) { // Paper - send full pos for hard colliding entities to prevent collision problems due to desync + if (!flag6 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.onGround()&& !(io.papermc.paper.configuration.GlobalConfiguration.get().collisions.sendFullPosForHardCollidingEntities && this.entity.hardCollides())) { // Paper - send full pos for hard colliding entities to prevent collision problems due to desync + if (flag2 || flag3 || this.entity instanceof AbstractArrow) { // Gale - Airplane - better checking for useless move packets if ((!flag2 || !flag3) && !(this.entity instanceof AbstractArrow)) { if (flag2) { - packet1 = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), this.entity.isOnGround()); -@@ -198,6 +199,7 @@ public class ServerEntity { + packet1 = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), this.entity.onGround()); +@@ -194,6 +195,7 @@ public class ServerEntity { flag4 = true; flag5 = true; } + } // Gale - Airplane - better checking for useless move packets } else { - this.wasOnGround = this.entity.isOnGround(); + this.wasOnGround = this.entity.onGround(); this.teleportDelay = 0; diff --git a/patches/server/0034-Use-fast-item-merge-raytracing.patch b/patches/server/0034-Use-fast-item-merge-raytracing.patch index 1dcf924..cd91cfc 100644 --- a/patches/server/0034-Use-fast-item-merge-raytracing.patch +++ b/patches/server/0034-Use-fast-item-merge-raytracing.patch @@ -31,21 +31,21 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -index d47b3ac633e7936d30abfda6fc46c2c7412d76fe..036d7d29d7c85390b9658ddd70f17447648c265a 100644 +index 3d41dbe0285f8fec8adae1e93010cf464df9b08c..cf2b2c279718d86cc2c57795ef2e61c835977564 100644 --- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -@@ -264,10 +264,16 @@ public class ItemEntity extends Entity implements TraceableEntity { +@@ -269,10 +269,16 @@ public class ItemEntity extends Entity implements TraceableEntity { if (entityitem.isMergable()) { // Paper Start - Fix items merging through walls - if (this.level.paperConfig().fixes.fixItemsMergingThroughWalls) { + if (this.level().paperConfig().fixes.fixItemsMergingThroughWalls) { + // Gale start - Airplane - use fast item merge raytracing - skip the allocations + /* net.minecraft.world.level.ClipContext rayTrace = new net.minecraft.world.level.ClipContext(this.position(), entityitem.position(), net.minecraft.world.level.ClipContext.Block.COLLIDER, net.minecraft.world.level.ClipContext.Fluid.NONE, this); - net.minecraft.world.phys.BlockHitResult rayTraceResult = level.clip(rayTrace); + net.minecraft.world.phys.BlockHitResult rayTraceResult = this.level().clip(rayTrace); if (rayTraceResult.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) continue; + */ -+ if (level.rayTraceDirect(this.position(), entityitem.position(), net.minecraft.world.phys.shapes.CollisionContext.of(this)) == ++ if (level().rayTraceDirect(this.position(), entityitem.position(), net.minecraft.world.phys.shapes.CollisionContext.of(this)) == + net.minecraft.world.phys.HitResult.Type.BLOCK) continue; + // Gale end - Airplane - use fast item merge raytracing - skip the allocations } diff --git a/patches/server/0036-Inline-level-height.patch b/patches/server/0036-Inline-level-height.patch index eba28a0..08d62ca 100644 --- a/patches/server/0036-Inline-level-height.patch +++ b/patches/server/0036-Inline-level-height.patch @@ -31,10 +31,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 67de286dd6a569da9f8b36a4c7bd673830c17a2b..901bd7f723b2c57ee5bbff0735fd6b47947f4383 100644 +index 5185af09005fd9f671daca0844b42e85b29a0376..dd5d28339d28f344c2e68b43782545e6378117ca 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -272,6 +272,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -271,6 +271,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public abstract ResourceKey getTypeKey(); @@ -50,7 +50,7 @@ index 67de286dd6a569da9f8b36a4c7bd673830c17a2b..901bd7f723b2c57ee5bbff0735fd6b47 protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, java.util.function.Function galeWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor // Gale - Gale configuration // Gale - Purpur - remove vanilla profiler this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper -@@ -294,6 +303,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -293,6 +302,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { }); final DimensionType dimensionmanager = (DimensionType) holder.value(); diff --git a/patches/server/0038-Remove-streams-and-iterators-from-range-check.patch b/patches/server/0038-Remove-streams-and-iterators-from-range-check.patch index 537a518..71009c5 100644 --- a/patches/server/0038-Remove-streams-and-iterators-from-range-check.patch +++ b/patches/server/0038-Remove-streams-and-iterators-from-range-check.patch @@ -31,10 +31,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index d7a8b1ee9c3efda2c8463d6e06e54eb59b1b4dc4..014ca25e17915d2a3a8e2a8d37265a4d7bc09d24 100644 +index 31963a0931bcd3465e46b9c875e89ac56aead62d..caa765c6264e14cc66973267f94074c7056fe122 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1630,8 +1630,30 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1549,8 +1549,30 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return ChunkMap.this.level.getServer().getScaledTrackingDistance(initialDistance); } @@ -65,7 +65,7 @@ index d7a8b1ee9c3efda2c8463d6e06e54eb59b1b4dc4..014ca25e17915d2a3a8e2a8d37265a4d Iterator iterator = this.entity.getIndirectPassengers().iterator(); while (iterator.hasNext()) { -@@ -1643,6 +1665,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1562,6 +1584,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider i = j; } } diff --git a/patches/server/0039-Remove-streams-from-getting-nearby-players.patch b/patches/server/0039-Remove-streams-from-getting-nearby-players.patch index ef8f2b9..cb2ad21 100644 --- a/patches/server/0039-Remove-streams-from-getting-nearby-players.patch +++ b/patches/server/0039-Remove-streams-from-getting-nearby-players.patch @@ -31,10 +31,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 158fecd37dedec27a930e427550a92c4db01fc8c..cd6396ccbfab14a456952462af580c75c4b73744 100644 +index cc6bcffcc124f75bdaf4e72601d1557a173b7825..ba832679bd88779eeea9ba17ff4b1bb164496d56 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -491,17 +491,37 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -495,17 +495,37 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { this.isLegacyTrackingEntity = isLegacyTrackingEntity; } @@ -73,7 +73,7 @@ index 158fecd37dedec27a930e427550a92c4db01fc8c..cd6396ccbfab14a456952462af580c75 for (Entity passenger : passengers) { org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType; int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal()); -@@ -510,6 +530,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -514,6 +534,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { range = passengerRange; } } diff --git a/patches/server/0041-Block-goal-does-not-load-chunks.patch b/patches/server/0040-Block-goal-does-not-load-chunks.patch similarity index 85% rename from patches/server/0041-Block-goal-does-not-load-chunks.patch rename to patches/server/0040-Block-goal-does-not-load-chunks.patch index bc50932..c750053 100644 --- a/patches/server/0041-Block-goal-does-not-load-chunks.patch +++ b/patches/server/0040-Block-goal-does-not-load-chunks.patch @@ -31,14 +31,14 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -index 26bf383caea68834c654b25653ced9017f1b1b22..6c67eef93e764ab7d339859d87d2e438b3b137fa 100644 +index 34f319ad09276c6f68dde449c79351de0d7d86f5..faa95db38528b24f2274e92f71e3001a640a428b 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java +++ b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java @@ -119,6 +119,7 @@ public abstract class MoveToBlockGoal extends Goal { for(int m = 0; m <= l; m = m > 0 ? -m : 1 - m) { for(int n = m < l && m > -l ? l : 0; n <= l; n = n > 0 ? -n : 1 - n) { mutableBlockPos.setWithOffset(blockPos, m, k - 1, n); -+ if (!this.mob.level.hasChunkAt(mutableBlockPos)) continue; // Gale - Airplane - block goal does not load chunks - if this block isn't loaded, continue - if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level, mutableBlockPos)) { ++ if (!this.mob.level().hasChunkAt(mutableBlockPos)) continue; // Gale - Airplane - block goal does not load chunks - if this block isn't loaded, continue + if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) { this.blockPos = mutableBlockPos; setTargetPosition(mutableBlockPos.immutable()); // Paper diff --git a/patches/server/0040-Skip-cloning-loot-parameters.patch b/patches/server/0040-Skip-cloning-loot-parameters.patch deleted file mode 100644 index 96820b7..0000000 --- a/patches/server/0040-Skip-cloning-loot-parameters.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Wed, 23 Nov 2022 23:01:31 +0100 -Subject: [PATCH] Skip cloning loot parameters - -License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) -Gale - https://galemc.org - -This patch is based on the following patch: -"Skip cloning loot parameters" -By: Paul Sauve -As part of: Airplane (https://github.com/TECHNOVE/Airplane) -Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) - -* Airplane description * - -Small improvement in CPU, much larger improvement in allocations. As a -new loot context is created every time a player moves (along with a lot -of other times) the constant cloning churns out a lot of useless -objects. - -* Airplane copyright * - -Airplane -Copyright (C) 2020 Technove LLC - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -diff --git a/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java b/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java -index 35f9b11a3a61976c952a2c1c64bb2a932538f54f..eb399244af70c8fe735657d429d883c48215af0a 100644 ---- a/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java -+++ b/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java -@@ -41,8 +41,10 @@ public class LootContext { - this.level = world; - this.lootTables = tableGetter; - this.conditions = conditionGetter; -- this.params = ImmutableMap.copyOf(parameters); -- this.dynamicDrops = ImmutableMap.copyOf(drops); -+ // Gale start - Airplane - skip cloning loot parameters - use unmodifiable maps instead of immutable ones to skip the copy -+ this.params = java.util.Collections.unmodifiableMap(parameters); -+ this.dynamicDrops = java.util.Collections.unmodifiableMap(drops); -+ // Gale end - Airplane - skip cloning loot parameters - use unmodifiable maps instead of immutable ones to skip the copy - } - - public boolean hasParam(LootContextParam parameter) { diff --git a/patches/server/0042-Reduce-entity-allocations.patch b/patches/server/0041-Reduce-entity-allocations.patch similarity index 95% rename from patches/server/0042-Reduce-entity-allocations.patch rename to patches/server/0041-Reduce-entity-allocations.patch index a35e1a8..67e1270 100644 --- a/patches/server/0042-Reduce-entity-allocations.patch +++ b/patches/server/0041-Reduce-entity-allocations.patch @@ -31,10 +31,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index cd6396ccbfab14a456952462af580c75c4b73744..f27df41a95926da2c38d2a5224d7597d17b620b0 100644 +index ba832679bd88779eeea9ba17ff4b1bb164496d56..6bc97a0ffbe9581d72f033fe315e1e2a03099e5b 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -417,6 +417,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -421,6 +421,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { return this.originWorld; } // Paper end diff --git a/patches/server/0043-Remove-lambda-from-ticking-guard.patch b/patches/server/0042-Remove-lambda-from-ticking-guard.patch similarity index 88% rename from patches/server/0043-Remove-lambda-from-ticking-guard.patch rename to patches/server/0042-Remove-lambda-from-ticking-guard.patch index cbfbb8f..83dc333 100644 --- a/patches/server/0043-Remove-lambda-from-ticking-guard.patch +++ b/patches/server/0042-Remove-lambda-from-ticking-guard.patch @@ -31,10 +31,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index cb0df7b5db3fa885f43b35e6ef8c6cdc221b4003..ef0b765c04bfdb684d8a3aef7aa4184fafb38864 100644 +index b7318670e039e05a058e437e025519d40557bb88..efbc8483a51c5272ca81747c82dc65e7f0f5c232 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -709,7 +709,20 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -850,7 +850,20 @@ public class ServerLevel extends Level implements WorldGenLevel { entity.stopRiding(); } @@ -46,7 +46,7 @@ index cb0df7b5db3fa885f43b35e6ef8c6cdc221b4003..ef0b765c04bfdb684d8a3aef7aa4184f + } catch (Throwable throwable) { + if (throwable instanceof ThreadDeath) throw throwable; // Paper + // Paper start - Prevent tile entity and entity crashes -+ final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); ++ final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level().getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); + MinecraftServer.LOGGER.error(msg, throwable); + getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable))); + entity.discard(); @@ -57,10 +57,10 @@ index cb0df7b5db3fa885f43b35e6ef8c6cdc221b4003..ef0b765c04bfdb684d8a3aef7aa4184f } } diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 901bd7f723b2c57ee5bbff0735fd6b47947f4383..d4527b0f11893925c93b1846305fb3b17ba8e89a 100644 +index dd5d28339d28f344c2e68b43782545e6378117ca..b9431be4e6fb366bbdb7e66c9570f26a37379488 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -1018,13 +1018,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1013,13 +1013,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { try { tickConsumer.accept(entity); MinecraftServer.getServer().executeMidTickTasks(); // Paper - execute chunk tasks mid tick @@ -68,7 +68,7 @@ index 901bd7f723b2c57ee5bbff0735fd6b47947f4383..d4527b0f11893925c93b1846305fb3b1 + } catch (Throwable throwable) { // Gale - Airplane - remove lambda from ticking guard - diff on change ServerLevel#tick if (throwable instanceof ThreadDeath) throw throwable; // Paper // Paper start - Prevent tile entity and entity crashes - final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); + final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level().getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); MinecraftServer.LOGGER.error(msg, throwable); getCraftServer().getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(msg, throwable))); - entity.discard(); diff --git a/patches/server/0044-Reduce-entity-fluid-lookups-if-no-fluids.patch b/patches/server/0043-Reduce-entity-fluid-lookups-if-no-fluids.patch similarity index 88% rename from patches/server/0044-Reduce-entity-fluid-lookups-if-no-fluids.patch rename to patches/server/0043-Reduce-entity-fluid-lookups-if-no-fluids.patch index 746af3e..0e6aab1 100644 --- a/patches/server/0044-Reduce-entity-fluid-lookups-if-no-fluids.patch +++ b/patches/server/0043-Reduce-entity-fluid-lookups-if-no-fluids.patch @@ -31,10 +31,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index f27df41a95926da2c38d2a5224d7597d17b620b0..a105db86db91d932d7d27fc6a96a5ef3c60671e5 100644 +index 6bc97a0ffbe9581d72f033fe315e1e2a03099e5b..eefbbd4621454b95ef0601be526e71c3d759446a 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -4171,16 +4171,18 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -4300,16 +4300,18 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { } public boolean updateFluidHeightAndDoFluidPushing(TagKey tag, double speed) { @@ -60,7 +60,7 @@ index f27df41a95926da2c38d2a5224d7597d17b620b0..a105db86db91d932d7d27fc6a96a5ef3 double d1 = 0.0D; boolean flag = this.isPushedByFluid(); boolean flag1 = false; -@@ -4188,14 +4190,61 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -4317,14 +4319,61 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { int k1 = 0; BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(); @@ -68,10 +68,10 @@ index f27df41a95926da2c38d2a5224d7597d17b620b0..a105db86db91d932d7d27fc6a96a5ef3 - for (int i2 = k; i2 < l; ++i2) { - for (int j2 = i1; j2 < j1; ++j2) { - blockposition_mutableblockposition.set(l1, i2, j2); -- FluidState fluid = this.level.getFluidState(blockposition_mutableblockposition); +- FluidState fluid = this.level().getFluidState(blockposition_mutableblockposition); + // Gale start - Airplane - reduce entity fluid lookups if no fluids - based off CollisionUtil.getCollisionsForBlocksOrWorldBorder -+ final int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this.level); -+ final int maxSection = io.papermc.paper.util.WorldUtil.getMaxSection(this.level); ++ final int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this.level()); ++ final int maxSection = io.papermc.paper.util.WorldUtil.getMaxSection(this.level()); + final int minBlock = minSection << 4; + final int maxBlock = (maxSection << 4) | 15; + @@ -98,7 +98,7 @@ index f27df41a95926da2c38d2a5224d7597d17b620b0..a105db86db91d932d7d27fc6a96a5ef3 + int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk + int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 16; // coordinate in chunk + -+ net.minecraft.world.level.chunk.ChunkAccess chunk = this.level.getChunkIfLoadedImmediately(currChunkX, currChunkZ); ++ net.minecraft.world.level.chunk.ChunkAccess chunk = this.level().getChunkIfLoadedImmediately(currChunkX, currChunkZ); + if (chunk == null) { + return false; // if we're touching an unloaded chunk then it's false + } @@ -122,13 +122,13 @@ index f27df41a95926da2c38d2a5224d7597d17b620b0..a105db86db91d932d7d27fc6a96a5ef3 + FluidState fluid = blocks.get(currX & 15, currY & 15, currZ & 15).getFluidState(); if (fluid.is(tag)) { -- double d2 = (double) ((float) i2 + fluid.getHeight(this.level, blockposition_mutableblockposition)); +- double d2 = (double) ((float) i2 + fluid.getHeight(this.level(), blockposition_mutableblockposition)); + blockposition_mutableblockposition.set((currChunkX << 4) + currX, currY, (currChunkZ << 4) + currZ); -+ double d2 = (double) ((float) currY + fluid.getHeight(this.level, blockposition_mutableblockposition)); ++ double d2 = (double) ((float) currY + fluid.getHeight(this.level(), blockposition_mutableblockposition)); if (d2 >= axisalignedbb.minY) { flag1 = true; -@@ -4217,9 +4266,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -4346,9 +4395,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { // CraftBukkit end } } @@ -142,18 +142,18 @@ index f27df41a95926da2c38d2a5224d7597d17b620b0..a105db86db91d932d7d27fc6a96a5ef3 if (vec3d.length() > 0.0D) { if (k1 > 0) { diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -index 854865f28bcae0fb0c17717c914687f78e951e21..0b4afcbe5a8c25418c28ce95961dd9e337c6973a 100644 +index d4477b0dda6a1ef7bd8323c0d11b636bd071d18e..e04749d26cc09aa3e1319a1f792085ad96f51b8c 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -27,6 +27,7 @@ public class LevelChunkSection { +@@ -25,6 +25,7 @@ public class LevelChunkSection { public final PalettedContainer states; // CraftBukkit start - read/write private PalettedContainer> biomes; + public short fluidStateCount; // Gale - Airplane - reduce entity fluid lookups if no fluids public final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper - public LevelChunkSection(int i, PalettedContainer datapaletteblock, PalettedContainer> palettedcontainerro) { -@@ -198,6 +199,7 @@ public class LevelChunkSection { + public LevelChunkSection(PalettedContainer datapaletteblock, PalettedContainer> palettedcontainerro) { +@@ -190,6 +191,7 @@ public class LevelChunkSection { if (!fluid.isEmpty()) { --this.tickingFluidCount; @@ -161,7 +161,7 @@ index 854865f28bcae0fb0c17717c914687f78e951e21..0b4afcbe5a8c25418c28ce95961dd9e3 } if (!state.isAir()) { -@@ -212,6 +214,7 @@ public class LevelChunkSection { +@@ -204,6 +206,7 @@ public class LevelChunkSection { if (!fluid1.isEmpty()) { ++this.tickingFluidCount; @@ -169,7 +169,7 @@ index 854865f28bcae0fb0c17717c914687f78e951e21..0b4afcbe5a8c25418c28ce95961dd9e3 } this.updateKnownBlockInfo(x | (z << 4) | (y << 8), iblockdata1, state); // Paper -@@ -261,6 +264,7 @@ public class LevelChunkSection { +@@ -249,6 +252,7 @@ public class LevelChunkSection { if (fluid.isRandomlyTicking()) { this.tickingFluidCount = (short) (this.tickingFluidCount + 1); } diff --git a/patches/server/0045-SIMD-support.patch b/patches/server/0044-SIMD-support.patch similarity index 94% rename from patches/server/0045-SIMD-support.patch rename to patches/server/0044-SIMD-support.patch index bb22d08..360a363 100644 --- a/patches/server/0045-SIMD-support.patch +++ b/patches/server/0044-SIMD-support.patch @@ -13,7 +13,7 @@ As part of: Pufferfish (https://github.com/pufferfish-gg/Pufferfish) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/build.gradle.kts b/build.gradle.kts -index 7d3b8f3c7616b5db72a6fccb51e5543a0e096125..394b5044090c1c7b00b30036e4b8be4a6ac58ed1 100644 +index 774e79681c910dc036238b4a913fa6ecaf09a94f..3097e021e89bf04e9cf18dae1f9be8a089d29743 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -63,6 +63,7 @@ tasks.withType { @@ -24,7 +24,7 @@ index 7d3b8f3c7616b5db72a6fccb51e5543a0e096125..394b5044090c1c7b00b30036e4b8be4a } // Gale end - hide irrelevant compilation warnings -@@ -186,6 +187,7 @@ fun TaskContainer.registerRunTask( +@@ -187,6 +188,7 @@ fun TaskContainer.registerRunTask( jvmArgs("--enable-preview") jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") // Gale end - enable virtual threads for development runs @@ -33,7 +33,7 @@ index 7d3b8f3c7616b5db72a6fccb51e5543a0e096125..394b5044090c1c7b00b30036e4b8be4a doFirst { workingDir.mkdirs() diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index 985f1a50438b325e33fba5d07e26519171504aab..54458601bbda3a4142c09f717e3b87a079ce096f 100644 +index 5be4267d88604210b7bfcc03b2c2056e0a9f0fb0..e981740075e287ede989e805314a1356b566a2c4 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -14,6 +14,8 @@ import java.util.Locale; diff --git a/patches/server/0046-Make-book-writing-configurable.patch b/patches/server/0045-Make-book-writing-configurable.patch similarity index 97% rename from patches/server/0046-Make-book-writing-configurable.patch rename to patches/server/0045-Make-book-writing-configurable.patch index d93f640..58a9866 100644 --- a/patches/server/0046-Make-book-writing-configurable.patch +++ b/patches/server/0045-Make-book-writing-configurable.patch @@ -22,7 +22,7 @@ you to easily disable books, should you want to preemptively remove this functionality before additional exploits are found. diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 4b74da1c0138ed5946c7f446be79c647c172faa5..10bdc027ad7070e2ee0e5b1b24783c3e880996dd 100644 +index 3d33236932c14d651d393656b06ae8152cc89eee..bb68e65813f2f471add275023f53430c05170813 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -184,6 +184,8 @@ import net.minecraft.world.phys.Vec3; @@ -34,7 +34,7 @@ index 4b74da1c0138ed5946c7f446be79c647c172faa5..10bdc027ad7070e2ee0e5b1b24783c3e import org.slf4j.Logger; // CraftBukkit start -@@ -1214,6 +1216,11 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic +@@ -1217,6 +1219,11 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic @Override public void handleEditBook(ServerboundEditBookPacket packet) { diff --git a/patches/server/0047-Optimize-entity-coordinate-key.patch b/patches/server/0046-Optimize-entity-coordinate-key.patch similarity index 90% rename from patches/server/0047-Optimize-entity-coordinate-key.patch rename to patches/server/0046-Optimize-entity-coordinate-key.patch index f2064e7..87aa814 100644 --- a/patches/server/0047-Optimize-entity-coordinate-key.patch +++ b/patches/server/0046-Optimize-entity-coordinate-key.patch @@ -22,7 +22,7 @@ data is already available in the blockPosition struct, so we use that instead of re-doing the casting. diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java -index 6efb8b10f17c70b05128039376d254e6beda3841..fe8d2fa5fdb116762b644b08020fee23e517e57c 100644 +index 2d11a67bdc82088abf0b3ca134f352f155c8eb1f..a31d7c1907f40792eefe35038bc6ec7cb67b2f6a 100644 --- a/src/main/java/io/papermc/paper/util/MCUtil.java +++ b/src/main/java/io/papermc/paper/util/MCUtil.java @@ -210,7 +210,7 @@ public final class MCUtil { @@ -35,10 +35,10 @@ index 6efb8b10f17c70b05128039376d254e6beda3841..fe8d2fa5fdb116762b644b08020fee23 public static long getCoordinateKey(final ChunkPos pair) { diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index a105db86db91d932d7d27fc6a96a5ef3c60671e5..254feb89dc2bdb7c994fb65b1a345f879ec8ff72 100644 +index eefbbd4621454b95ef0601be526e71c3d759446a..eb395d6498f3f43c20d371efd8e1f410400f1f2a 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -292,7 +292,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -294,7 +294,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { public double yo; public double zo; private Vec3 position; diff --git a/patches/server/0048-Reduce-in-wall-checks.patch b/patches/server/0047-Reduce-in-wall-checks.patch similarity index 85% rename from patches/server/0048-Reduce-in-wall-checks.patch rename to patches/server/0047-Reduce-in-wall-checks.patch index f1076cf..7d3c680 100644 --- a/patches/server/0048-Reduce-in-wall-checks.patch +++ b/patches/server/0047-Reduce-in-wall-checks.patch @@ -28,22 +28,22 @@ but is so much cheaper than the suffocation check that it's worth keeping it. diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 6411a059a67e1a09580ec8b11afbffe1ef4b8958..848022c049e27d80ceb11f0cee6cbda15edf2ff3 100644 +index 806bf3e93ec368d117851281fd5d51fa22e3a4bf..1040e76c829521b214e5a96fb41ee60e223641d3 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -395,7 +395,10 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -413,7 +413,10 @@ public abstract class LivingEntity extends Entity implements Attackable { boolean flag = this instanceof net.minecraft.world.entity.player.Player; - if (!this.level.isClientSide) { + if (!this.level().isClientSide) { - if (this.isInWall()) { + // Gale start - Pufferfish - reduce in wall checks -+ long checkStuckInWallInterval = this.level.galeConfig().smallOptimizations.reducedIntervals.checkStuckInWall; ++ long checkStuckInWallInterval = this.level().galeConfig().smallOptimizations.reducedIntervals.checkStuckInWall; + if ((checkStuckInWallInterval <= 1 || (tickCount % checkStuckInWallInterval == 0 && couldPossiblyBeHurt(1.0F))) && this.isInWall()) { + // Gale end - Pufferfish - reduce in wall checks this.hurt(this.damageSources().inWall(), 1.0F); - } else if (flag && !this.level.getWorldBorder().isWithinBounds(this.getBoundingBox())) { - double d0 = this.level.getWorldBorder().getDistanceToBorder(this) + this.level.getWorldBorder().getDamageSafeZone(); -@@ -1318,6 +1321,15 @@ public abstract class LivingEntity extends Entity implements Attackable { + } else if (flag && !this.level().getWorldBorder().isWithinBounds(this.getBoundingBox())) { + double d0 = this.level().getWorldBorder().getDistanceToBorder(this) + this.level().getWorldBorder().getDamageSafeZone(); +@@ -1367,6 +1370,15 @@ public abstract class LivingEntity extends Entity implements Attackable { return this.getHealth() <= 0.0F; } diff --git a/patches/server/0049-Make-chat-order-verification-configurable.patch b/patches/server/0048-Make-chat-order-verification-configurable.patch similarity index 94% rename from patches/server/0049-Make-chat-order-verification-configurable.patch rename to patches/server/0048-Make-chat-order-verification-configurable.patch index d2fd2c7..146b775 100644 --- a/patches/server/0049-Make-chat-order-verification-configurable.patch +++ b/patches/server/0048-Make-chat-order-verification-configurable.patch @@ -13,10 +13,10 @@ As part of: Pufferfish (https://github.com/pufferfish-gg/Pufferfish) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 10bdc027ad7070e2ee0e5b1b24783c3e880996dd..434a671878fedb3dea7423a929def187a9ef4f9c 100644 +index bb68e65813f2f471add275023f53430c05170813..9b028f8a5d0bcda9ea1bc28b65587627e31e78b1 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2368,6 +2368,11 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic +@@ -2366,6 +2366,11 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic } private boolean updateChatOrder(Instant timestamp) { diff --git a/patches/server/0050-Make-saving-fireworks-configurable.patch b/patches/server/0049-Make-saving-fireworks-configurable.patch similarity index 94% rename from patches/server/0050-Make-saving-fireworks-configurable.patch rename to patches/server/0049-Make-saving-fireworks-configurable.patch index 4d491e5..5589663 100644 --- a/patches/server/0050-Make-saving-fireworks-configurable.patch +++ b/patches/server/0049-Make-saving-fireworks-configurable.patch @@ -20,7 +20,7 @@ launcher can very easily fill a chunk. Prevent saving Fireworks so that chunk unloads will wipe a chunks fireworks in this case. diff --git a/src/main/java/net/minecraft/world/entity/projectile/FireworkRocketEntity.java b/src/main/java/net/minecraft/world/entity/projectile/FireworkRocketEntity.java -index fca27f98989bf106060ba08196255fe32f850df5..2260bc728ce6cdbc78408aec37ac3ffe26db3d76 100644 +index 288910fb168ddc5d3a61971778b8038a56772fa8..a9354adc79b71a206ef7d76f0c59b26478160fb6 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/FireworkRocketEntity.java +++ b/src/main/java/net/minecraft/world/entity/projectile/FireworkRocketEntity.java @@ -357,4 +357,12 @@ public class FireworkRocketEntity extends Projectile implements ItemSupplier { @@ -31,7 +31,7 @@ index fca27f98989bf106060ba08196255fe32f850df5..2260bc728ce6cdbc78408aec37ac3ffe + // Gale start - EMC - make saving fireworks configurable + @Override + public boolean shouldBeSaved() { -+ return this.level.galeConfig().smallOptimizations.saveFireworks; ++ return this.level().galeConfig().smallOptimizations.saveFireworks; + } + // Gale end - EMC - make saving fireworks configurable + diff --git a/patches/server/0051-Don-t-trigger-lootable-refresh-for-non-player-intera.patch b/patches/server/0050-Don-t-trigger-lootable-refresh-for-non-player-intera.patch similarity index 91% rename from patches/server/0051-Don-t-trigger-lootable-refresh-for-non-player-intera.patch rename to patches/server/0050-Don-t-trigger-lootable-refresh-for-non-player-intera.patch index aff66e9..1d33177 100644 --- a/patches/server/0051-Don-t-trigger-lootable-refresh-for-non-player-intera.patch +++ b/patches/server/0050-Don-t-trigger-lootable-refresh-for-non-player-intera.patch @@ -13,7 +13,7 @@ As part of: EmpireCraft (https://github.com/starlis/empirecraft) Licensed under: MIT (https://opensource.org/licenses/MIT) diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -index a05f7c8815d564bf1b95b6ce35ca2ac71ec84c9f..6e75f5696a6bc56a26578dba518920ac9b069427 100644 +index 6347459c1e7347914642c23fe919526db879d103..d652873922fa89a40f7520f5aff53847637c9771 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java @@ -70,6 +70,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc @@ -22,5 +22,5 @@ index a05f7c8815d564bf1b95b6ce35ca2ac71ec84c9f..6e75f5696a6bc56a26578dba518920ac public void unpackLootTable(@Nullable Player player) { + if (player == null) return; // Gale - EMC - don't trigger lootable refresh for non-player interaction if (this.lootableData.shouldReplenish(player) && this.level.getServer() != null) { // Paper - LootTable lootTable = this.level.getServer().getLootTables().get(this.lootTable); + LootTable lootTable = this.level.getServer().getLootData().getLootTable(this.lootTable); if (player instanceof ServerPlayer) { diff --git a/patches/server/0052-Reduce-hopper-item-checks.patch b/patches/server/0051-Reduce-hopper-item-checks.patch similarity index 93% rename from patches/server/0052-Reduce-hopper-item-checks.patch rename to patches/server/0051-Reduce-hopper-item-checks.patch index 232c627..b553b91 100644 --- a/patches/server/0052-Reduce-hopper-item-checks.patch +++ b/patches/server/0051-Reduce-hopper-item-checks.patch @@ -17,7 +17,7 @@ Licensed under: MIT (https://opensource.org/licenses/MIT) Only do an item "suck in" action once per second diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -index 036d7d29d7c85390b9658ddd70f17447648c265a..b77d112f74046cd35f17bada00a22402cb716482 100644 +index cf2b2c279718d86cc2c57795ef2e61c835977564..fb0cd2ad93d2dbd678662f2cdad0851fd698cf3d 100644 --- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java @@ -15,11 +15,13 @@ import net.minecraft.world.entity.EntityType; @@ -47,14 +47,14 @@ index 036d7d29d7c85390b9658ddd70f17447648c265a..b77d112f74046cd35f17bada00a22402 + // Gale start - EMC - reduce hopper item checks + private void markNearbyHopperCartsAsImmune() { -+ var config = level.galeConfig().smallOptimizations.reducedIntervals.checkNearbyItem.hopper.minecart; ++ var config = level().galeConfig().smallOptimizations.reducedIntervals.checkNearbyItem.hopper.minecart; + // No need to mark hopper minecarts as immune if they can pull every tick anyway + if (config.interval <= 1) { + return; + } + if (config.temporaryImmunity.duration > 0 && this.isAlive() && this.onGround && !this.isRemoved() && (config.temporaryImmunity.nearbyItemMaxAge == -1 || this.age <= config.temporaryImmunity.nearbyItemMaxAge) && this.age % Math.max(1, config.temporaryImmunity.checkForMinecartNearItemInterval) == 0 && config.temporaryImmunity.maxItemHorizontalDistance >= 0 && config.temporaryImmunity.maxItemVerticalDistance >= 0) { + AABB aabb = this.getBoundingBox().inflate(config.temporaryImmunity.maxItemHorizontalDistance, config.temporaryImmunity.maxItemVerticalDistance, config.temporaryImmunity.maxItemHorizontalDistance); -+ for (Entity entity : this.level.getEntities(this, aabb)) { ++ for (Entity entity : this.level().getEntities(this, aabb)) { + if (entity instanceof MinecartHopper) { + ((MinecartHopper) entity).pickupImmunity = MinecraftServer.currentTick + config.temporaryImmunity.duration; + } @@ -73,7 +73,7 @@ index 036d7d29d7c85390b9658ddd70f17447648c265a..b77d112f74046cd35f17bada00a22402 + return; // Gale - EMC - reduce hopper item checks + } + // Gale start - EMC - reduce hopper item checks -+ if (level.galeConfig().smallOptimizations.reducedIntervals.checkNearbyItem.hopper.minecart.temporaryImmunity.checkForMinecartNearItemWhileInactive) { ++ if (level().galeConfig().smallOptimizations.reducedIntervals.checkNearbyItem.hopper.minecart.temporaryImmunity.checkForMinecartNearItemWhileInactive) { + this.markNearbyHopperCartsAsImmune(); } + // Gale end - EMC - reduce hopper item checks @@ -81,7 +81,7 @@ index 036d7d29d7c85390b9658ddd70f17447648c265a..b77d112f74046cd35f17bada00a22402 // Spigot end diff --git a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java -index 1b8f22805af87dc08e0dea9fd93a5f93c0b05107..36c2da765ef520d0cbe239892b8234b81b1b8012 100644 +index fc35cfc9d045f3e5b6a50af1d0ba83b6e322091f..41e23b4e0df3f3561925dad87624f4e445831a52 100644 --- a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java +++ b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java @@ -17,6 +17,7 @@ import net.minecraft.world.level.block.state.BlockState; @@ -119,7 +119,7 @@ index a05acf709735b40ca86f978508c63a86065fd405..3752b4ba7fb0d680d4b4a61bf44d54d5 double getLevelY(); diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java -index 1ad1716d27e58db05c1e22f7f2781319debde925..44732d98902797e3ed924bf77198657ce3a1ec30 100644 +index 1eb9326a0defaf412d94eac2e3b3e1fd855ba24c..f28e264c5975e94d284bdbd9646f3154a0e223ab 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java @@ -11,6 +11,7 @@ import net.minecraft.core.Direction; @@ -156,7 +156,7 @@ index 1ad1716d27e58db05c1e22f7f2781319debde925..44732d98902797e3ed924bf77198657c @io.papermc.paper.annotation.DoNotUse // Paper - method unused as logic is inlined above @@ -805,6 +808,31 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - return first.is(second.getItem()) && first.getDamageValue() == second.getDamageValue() && first.getCount() < first.getMaxStackSize() && ItemStack.tagMatches(first, second); // Paper - used to return true for full itemstacks?! + return first.getCount() < first.getMaxStackSize() && first.is(second.getItem()) && first.getDamageValue() == second.getDamageValue() && ((first.isEmpty() && second.isEmpty()) || java.util.Objects.equals(first.getTag(), second.getTag())); // Paper - used to return true for full itemstacks?! } + // Gale start - EMC - reduce hopper item checks diff --git a/patches/server/0053-Reduce-villager-item-re-pickup.patch b/patches/server/0052-Reduce-villager-item-re-pickup.patch similarity index 92% rename from patches/server/0053-Reduce-villager-item-re-pickup.patch rename to patches/server/0052-Reduce-villager-item-re-pickup.patch index ef8dc3f..60ee30e 100644 --- a/patches/server/0053-Reduce-villager-item-re-pickup.patch +++ b/patches/server/0052-Reduce-villager-item-re-pickup.patch @@ -18,7 +18,7 @@ Helps 1.8 Farms let hoppers pick it up before Villager due to our hopper changes diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java b/src/main/java/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java -index d81f7eecb8204a974da14a75d1a7a23a6c071112..3bff137413f12df37f1b77d68991e653c6267861 100644 +index 92bd58010e8c89e361e28aec59447349edbbc028..2239c7260638bb25df5b45dbc129bcfe1530bc59 100644 --- a/src/main/java/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java +++ b/src/main/java/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java @@ -21,6 +21,7 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; @@ -36,7 +36,7 @@ index d81f7eecb8204a974da14a75d1a7a23a6c071112..3bff137413f12df37f1b77d68991e653 - entityitem.setDefaultPickUpDelay(); + // Gale start - EMC - reduce villager item re-pickup + if (entity instanceof Villager) { -+ int repickupDelay = entity.level.galeConfig().smallOptimizations.reducedIntervals.villagerItemRepickup; ++ int repickupDelay = entity.level().galeConfig().smallOptimizations.reducedIntervals.villagerItemRepickup; + if (repickupDelay <= -1) { + entityitem.setDefaultPickUpDelay(); + } else { @@ -46,7 +46,7 @@ index d81f7eecb8204a974da14a75d1a7a23a6c071112..3bff137413f12df37f1b77d68991e653 + // Gale end - EMC - reduce villager item re-pickup // CraftBukkit start org.bukkit.event.entity.EntityDropItemEvent event = new org.bukkit.event.entity.EntityDropItemEvent(entity.getBukkitEntity(), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); - entityitem.level.getCraftServer().getPluginManager().callEvent(event); + entityitem.level().getCraftServer().getPluginManager().callEvent(event); diff --git a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java index 0f2f14b0612f764a2780bc244c89db548997b2c1..be563b466b9b9312254596ea3b8e116b28cf250c 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java diff --git a/patches/server/0054-Variable-entity-wake-up-duration.patch b/patches/server/0053-Variable-entity-wake-up-duration.patch similarity index 96% rename from patches/server/0054-Variable-entity-wake-up-duration.patch rename to patches/server/0053-Variable-entity-wake-up-duration.patch index 87e31a5..d8f2820 100644 --- a/patches/server/0054-Variable-entity-wake-up-duration.patch +++ b/patches/server/0053-Variable-entity-wake-up-duration.patch @@ -38,7 +38,7 @@ index be563b466b9b9312254596ea3b8e116b28cf250c..7355c828ab66c23d878e4981be9e44c7 + } diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index a04c202e83abc08a98549440e987d4d5f47379b7..754c8ab99b908b017b9ef4ceaa3ae67c7266ac44 100644 +index 713f62b7bfe290b1eb0eda6e3acc1d3583029aa6..4dc59a82f12a727f6db4a68bc1f5bd65c8cb08cc 100644 --- a/src/main/java/org/spigotmc/ActivationRange.java +++ b/src/main/java/org/spigotmc/ActivationRange.java @@ -38,6 +38,7 @@ import co.aikar.timings.MinecraftTimings; @@ -83,7 +83,7 @@ index a04c202e83abc08a98549440e987d4d5f47379b7..754c8ab99b908b017b9ef4ceaa3ae67c + private static final java.util.Random wakeUpDurationRandom = new java.util.Random(); + + private static int getWakeUpDurationWithVariance(Entity entity, int wakeUpDuration) { -+ GaleWorldConfiguration config = entity.level.galeConfig(); ++ GaleWorldConfiguration config = entity.level().galeConfig(); + double deviation = config.gameplayMechanics.entityWakeUpDurationRatioStandardDeviation; + if (deviation <= 0) { + return wakeUpDuration; diff --git a/patches/server/0055-Do-not-process-chat-commands-before-player-has-joine.patch b/patches/server/0054-Do-not-process-chat-commands-before-player-has-joine.patch similarity index 88% rename from patches/server/0055-Do-not-process-chat-commands-before-player-has-joine.patch rename to patches/server/0054-Do-not-process-chat-commands-before-player-has-joine.patch index 1b94cac..212b5c7 100644 --- a/patches/server/0055-Do-not-process-chat-commands-before-player-has-joine.patch +++ b/patches/server/0054-Do-not-process-chat-commands-before-player-has-joine.patch @@ -13,7 +13,7 @@ As part of: EmpireCraft (https://github.com/starlis/empirecraft) Licensed under: MIT (https://opensource.org/licenses/MIT) diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index ab208c49a5ed5db359ab8a4a12ddf91ecec2ce5d..9de7eda75e41065ab509c05c3ed844d943ccfe00 100644 +index f77f4cbdca0b22e0c24acbf08425e2e83ddef449..e7ec98be67ab50c06d90a1bccd023fcd2a2fee93 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java @@ -270,6 +270,7 @@ public class ServerPlayer extends Player { @@ -25,10 +25,10 @@ index ab208c49a5ed5db359ab8a4a12ddf91ecec2ce5d..9de7eda75e41065ab509c05c3ed844d9 // CraftBukkit end public boolean isRealPlayer; // Paper diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 434a671878fedb3dea7423a929def187a9ef4f9c..6e1567de96b77a8eea9f2a3bcb1756e391949ca8 100644 +index 9b028f8a5d0bcda9ea1bc28b65587627e31e78b1..ae50f9e62f1275d0cff6f017ec23e4ebcad46a1d 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2345,6 +2345,10 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic +@@ -2343,6 +2343,10 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales this.send(new ClientboundSystemChatPacket(PaperAdventure.asAdventure(Component.translatable("chat.disabled.options").withStyle(ChatFormatting.RED)), false)); // Paper - Adventure return Optional.empty(); @@ -40,10 +40,10 @@ index 434a671878fedb3dea7423a929def187a9ef4f9c..6e1567de96b77a8eea9f2a3bcb1756e3 this.player.resetLastActionTime(); return optional; diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 92e758a286a5db079c32d53cc52c8a422457daef..c86c06adc424577de49339760473758ac12d2000 100644 +index 683b5d78f9e3dc34e40c54683f64be32317797ac..e0e27beebffdbb8fa534899b451cc59f275faef0 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -335,6 +335,8 @@ public abstract class PlayerList { +@@ -342,6 +342,8 @@ public abstract class PlayerList { return; } diff --git a/patches/server/0056-Do-not-log-invalid-statistics.patch b/patches/server/0055-Do-not-log-invalid-statistics.patch similarity index 100% rename from patches/server/0056-Do-not-log-invalid-statistics.patch rename to patches/server/0055-Do-not-log-invalid-statistics.patch diff --git a/patches/server/0057-Do-not-log-empty-message-warnings.patch b/patches/server/0056-Do-not-log-empty-message-warnings.patch similarity index 93% rename from patches/server/0057-Do-not-log-empty-message-warnings.patch rename to patches/server/0056-Do-not-log-empty-message-warnings.patch index 79225e1..2239aa0 100644 --- a/patches/server/0057-Do-not-log-empty-message-warnings.patch +++ b/patches/server/0056-Do-not-log-empty-message-warnings.patch @@ -7,7 +7,7 @@ License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 6e1567de96b77a8eea9f2a3bcb1756e391949ca8..415162e8cee85ec04b8306f1e78caf463813b6fc 100644 +index ae50f9e62f1275d0cff6f017ec23e4ebcad46a1d..aeeda04840eb8f7b1c05456acecbf9f81fcef54d 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -185,6 +185,7 @@ import net.minecraft.world.phys.shapes.BooleanOp; @@ -18,7 +18,7 @@ index 6e1567de96b77a8eea9f2a3bcb1756e391949ca8..415162e8cee85ec04b8306f1e78caf46 import org.galemc.gale.configuration.GaleGlobalConfiguration; import org.slf4j.Logger; -@@ -2557,7 +2558,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic +@@ -2555,7 +2556,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic // CraftBukkit start String s = message.signedContent(); if (s.isEmpty()) { diff --git a/patches/server/0058-Do-not-log-ignored-advancements.patch b/patches/server/0057-Do-not-log-ignored-advancements.patch similarity index 97% rename from patches/server/0058-Do-not-log-ignored-advancements.patch rename to patches/server/0057-Do-not-log-ignored-advancements.patch index 1b60b43..7cea0d2 100644 --- a/patches/server/0058-Do-not-log-ignored-advancements.patch +++ b/patches/server/0057-Do-not-log-ignored-advancements.patch @@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java -index 26888ebd38280de92e41dd8006d2b24e874afe26..c953f7f2f125985eeec9563a22f9188cc979cd36 100644 +index acc49f66bf34e2507d0ee6fec0a56b11bfc68f46..8834a32bfb27653062d242144dcd75ce7e289abc 100644 --- a/src/main/java/net/minecraft/server/PlayerAdvancements.java +++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java @@ -43,6 +43,7 @@ import net.minecraft.server.level.ServerPlayer; diff --git a/patches/server/0059-Do-not-log-setBlock-in-far-chunks.patch b/patches/server/0058-Do-not-log-setBlock-in-far-chunks.patch similarity index 100% rename from patches/server/0059-Do-not-log-setBlock-in-far-chunks.patch rename to patches/server/0058-Do-not-log-setBlock-in-far-chunks.patch diff --git a/patches/server/0060-Do-not-log-unrecognized-recipes.patch b/patches/server/0059-Do-not-log-unrecognized-recipes.patch similarity index 96% rename from patches/server/0060-Do-not-log-unrecognized-recipes.patch rename to patches/server/0059-Do-not-log-unrecognized-recipes.patch index adab2fe..227a17c 100644 --- a/patches/server/0060-Do-not-log-unrecognized-recipes.patch +++ b/patches/server/0059-Do-not-log-unrecognized-recipes.patch @@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/stats/ServerRecipeBook.java b/src/main/java/net/minecraft/stats/ServerRecipeBook.java -index d13ed3069e944d138442ea440ac3eaf8d44c18d3..c89b8e5ea10a465160504f7364db47410ee5ec0d 100644 +index ea29e07a105f3ba6a878bdccf36e7eaf66280280..7b782f705b9f349e914a7192778d5e25bad635b8 100644 --- a/src/main/java/net/minecraft/stats/ServerRecipeBook.java +++ b/src/main/java/net/minecraft/stats/ServerRecipeBook.java @@ -18,6 +18,7 @@ import net.minecraft.resources.ResourceLocation; @@ -48,7 +48,7 @@ index d13ed3069e944d138442ea440ac3eaf8d44c18d3..c89b8e5ea10a465160504f7364db4741 import org.slf4j.Logger; import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit -@@ -122,7 +123,7 @@ public class ServerRecipeBook extends RecipeBook { +@@ -125,7 +126,7 @@ public class ServerRecipeBook extends RecipeBook { Optional> optional = recipeManager.byKey(minecraftkey); if (!optional.isPresent()) { diff --git a/patches/server/0061-Do-not-log-legacy-Material-initialization.patch b/patches/server/0060-Do-not-log-legacy-Material-initialization.patch similarity index 100% rename from patches/server/0061-Do-not-log-legacy-Material-initialization.patch rename to patches/server/0060-Do-not-log-legacy-Material-initialization.patch diff --git a/patches/server/0062-Do-not-log-expired-message-warnings.patch b/patches/server/0061-Do-not-log-expired-message-warnings.patch similarity index 100% rename from patches/server/0062-Do-not-log-expired-message-warnings.patch rename to patches/server/0061-Do-not-log-expired-message-warnings.patch diff --git a/patches/server/0063-Do-not-log-out-of-order-message-warnings.patch b/patches/server/0062-Do-not-log-out-of-order-message-warnings.patch similarity index 94% rename from patches/server/0063-Do-not-log-out-of-order-message-warnings.patch rename to patches/server/0062-Do-not-log-out-of-order-message-warnings.patch index 1134468..8917552 100644 --- a/patches/server/0063-Do-not-log-out-of-order-message-warnings.patch +++ b/patches/server/0062-Do-not-log-out-of-order-message-warnings.patch @@ -7,10 +7,10 @@ License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 415162e8cee85ec04b8306f1e78caf463813b6fc..a63236fb2995e8eea9185a19417dd6464bb832a0 100644 +index aeeda04840eb8f7b1c05456acecbf9f81fcef54d..fcf395497593704858439f7f9ca8b46b699c42df 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2335,7 +2335,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic +@@ -2333,7 +2333,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic private Optional tryHandleChat(String message, Instant timestamp, LastSeenMessages.Update acknowledgment) { if (!this.updateChatOrder(timestamp)) { diff --git a/patches/server/0064-Do-not-log-Not-Secure-marker.patch b/patches/server/0063-Do-not-log-Not-Secure-marker.patch similarity index 94% rename from patches/server/0064-Do-not-log-Not-Secure-marker.patch rename to patches/server/0063-Do-not-log-Not-Secure-marker.patch index b7e6d67..24f070c 100644 --- a/patches/server/0064-Do-not-log-Not-Secure-marker.patch +++ b/patches/server/0063-Do-not-log-Not-Secure-marker.patch @@ -7,7 +7,7 @@ License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/io/papermc/paper/adventure/ChatProcessor.java b/src/main/java/io/papermc/paper/adventure/ChatProcessor.java -index 951a7df30bd70bb3051c04f592529d560be6948e..42ac52d595308e36a4c7e98c0379408057f889a8 100644 +index 774fb97912f766589f3548f659618ad554e0503f..bec9a8ca016f23352ba51e5f70ecb9a0b66b0385 100644 --- a/src/main/java/io/papermc/paper/adventure/ChatProcessor.java +++ b/src/main/java/io/papermc/paper/adventure/ChatProcessor.java @@ -43,6 +43,7 @@ import org.bukkit.event.player.PlayerChatEvent; @@ -28,7 +28,7 @@ index 951a7df30bd70bb3051c04f592529d560be6948e..42ac52d595308e36a4c7e98c03794080 } diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index c86c06adc424577de49339760473758ac12d2000..31554b46b0e6c84d8b480b10e1c46649d2518488 100644 +index e0e27beebffdbb8fa534899b451cc59f275faef0..efc958959b772312acfbcfb0cf52c6aa6be77fb2 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -101,6 +101,7 @@ import net.minecraft.world.scores.Objective; @@ -39,7 +39,7 @@ index c86c06adc424577de49339760473758ac12d2000..31554b46b0e6c84d8b480b10e1c46649 import org.slf4j.Logger; // CraftBukkit start -@@ -1396,7 +1397,7 @@ public abstract class PlayerList { +@@ -1403,7 +1404,7 @@ public abstract class PlayerList { // Paper end boolean flag = this.verifyChatTrusted(message); diff --git a/patches/server/0065-Do-not-log-run-as-root-warning.patch b/patches/server/0064-Do-not-log-run-as-root-warning.patch similarity index 96% rename from patches/server/0065-Do-not-log-run-as-root-warning.patch rename to patches/server/0064-Do-not-log-run-as-root-warning.patch index e6af218..3833208 100644 --- a/patches/server/0065-Do-not-log-run-as-root-warning.patch +++ b/patches/server/0064-Do-not-log-run-as-root-warning.patch @@ -41,7 +41,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index 54458601bbda3a4142c09f717e3b87a079ce096f..afdd597c63703cca87b277f0b66786cd8f3bf9aa 100644 +index e981740075e287ede989e805314a1356b566a2c4..84ffee8934381ba380a2a8cf936968464df7a71e 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -179,7 +179,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface diff --git a/patches/server/0066-Do-not-log-offline-mode-warning.patch b/patches/server/0065-Do-not-log-offline-mode-warning.patch similarity index 96% rename from patches/server/0066-Do-not-log-offline-mode-warning.patch rename to patches/server/0065-Do-not-log-offline-mode-warning.patch index cbbd9c3..238222c 100644 --- a/patches/server/0066-Do-not-log-offline-mode-warning.patch +++ b/patches/server/0065-Do-not-log-offline-mode-warning.patch @@ -41,7 +41,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index afdd597c63703cca87b277f0b66786cd8f3bf9aa..5a92a8f24d3a6957cc3f8404bf3458f4c4058fb4 100644 +index 84ffee8934381ba380a2a8cf936968464df7a71e..cafc4391d2ed3df7b0b8d73c663777cdbb3a5de9 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -297,7 +297,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface diff --git a/patches/server/0067-Softly-log-invalid-pool-element-errors.patch b/patches/server/0066-Softly-log-invalid-pool-element-errors.patch similarity index 100% rename from patches/server/0067-Softly-log-invalid-pool-element-errors.patch rename to patches/server/0066-Softly-log-invalid-pool-element-errors.patch diff --git a/patches/server/0068-Fix-legacy-colors-in-console.patch b/patches/server/0067-Fix-legacy-colors-in-console.patch similarity index 100% rename from patches/server/0068-Fix-legacy-colors-in-console.patch rename to patches/server/0067-Fix-legacy-colors-in-console.patch diff --git a/patches/server/0069-Fix-outdated-server-showing-in-ping-before-server-fu.patch b/patches/server/0068-Fix-outdated-server-showing-in-ping-before-server-fu.patch similarity index 100% rename from patches/server/0069-Fix-outdated-server-showing-in-ping-before-server-fu.patch rename to patches/server/0068-Fix-outdated-server-showing-in-ping-before-server-fu.patch diff --git a/patches/server/0070-Make-sand-duping-fix-configurable.patch b/patches/server/0069-Make-sand-duping-fix-configurable.patch similarity index 89% rename from patches/server/0070-Make-sand-duping-fix-configurable.patch rename to patches/server/0069-Make-sand-duping-fix-configurable.patch index 632641f..ad681d2 100644 --- a/patches/server/0070-Make-sand-duping-fix-configurable.patch +++ b/patches/server/0069-Make-sand-duping-fix-configurable.patch @@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index 536856300da929c101f50da5827677bada5feb50..3d05bdbaf3488b5a01d949b9e90b1ebed935adc3 100644 +index eff81e846f696349b3bd3d26c02442f157b169f0..084a8f42ede16a193ce2a2dae932171424fbc3fb 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -131,7 +131,7 @@ public class FallingBlockEntity extends Entity { @@ -45,7 +45,7 @@ index 536856300da929c101f50da5827677bada5feb50..3d05bdbaf3488b5a01d949b9e90b1ebe public void tick() { // Paper start - fix sand duping - if (this.isRemoved()) { -+ if (this.level.galeConfig().gameplayMechanics.fixes.sandDuping && this.isRemoved()) { // Gale - Purpur - make sand duping fix configurable ++ if (this.level().galeConfig().gameplayMechanics.fixes.sandDuping && this.isRemoved()) { // Gale - Purpur - make sand duping fix configurable return; } // Paper end - fix sand duping @@ -54,7 +54,7 @@ index 536856300da929c101f50da5827677bada5feb50..3d05bdbaf3488b5a01d949b9e90b1ebe // Paper start - fix sand duping - if (this.isRemoved()) { -+ if (this.level.galeConfig().gameplayMechanics.fixes.sandDuping && this.isRemoved()) { // Gale - Purpur - make sand duping fix configurable ++ if (this.level().galeConfig().gameplayMechanics.fixes.sandDuping && this.isRemoved()) { // Gale - Purpur - make sand duping fix configurable return; } // Paper end - fix sand duping diff --git a/patches/server/0071-Fix-MC-238526.patch b/patches/server/0070-Fix-MC-238526.patch similarity index 97% rename from patches/server/0071-Fix-MC-238526.patch rename to patches/server/0070-Fix-MC-238526.patch index ddb6893..05e0118 100644 --- a/patches/server/0071-Fix-MC-238526.patch +++ b/patches/server/0070-Fix-MC-238526.patch @@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java b/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java -index 35cfa366baf6747105faa93f1220bb9cc31a5bd5..2fcdb8173db167a440a5a94d1ec8f6c2eeb08c0b 100644 +index cd2ce5bcb8c30e4657cd0e340d80544c7e805905..db3574e4ec9bad32bbbcd9d7ab9607da60e1715a 100644 --- a/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java +++ b/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java @@ -82,6 +82,6 @@ public abstract class WaterAnimal extends PathfinderMob { diff --git a/patches/server/0072-Fix-MC-123848.patch b/patches/server/0071-Fix-MC-123848.patch similarity index 95% rename from patches/server/0072-Fix-MC-123848.patch rename to patches/server/0071-Fix-MC-123848.patch index 5d43448..b0f3100 100644 --- a/patches/server/0072-Fix-MC-123848.patch +++ b/patches/server/0071-Fix-MC-123848.patch @@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java -index 30aec9dff249ae629b22318e52902361a9fa4099..c58fab79c9425a56cd9ffdfa81e4ea97d5dff941 100644 +index 955316687e2e29ad75a0052317a7b0f89034c82a..4fca705a68422e3829d82e024cbadfb077710a6d 100644 --- a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java +++ b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java @@ -287,6 +287,16 @@ public class ItemFrame extends HangingEntity { @@ -47,7 +47,7 @@ index 30aec9dff249ae629b22318e52902361a9fa4099..c58fab79c9425a56cd9ffdfa81e4ea97 + // Gale start - Purpur - fix MC-123848 + @Override + public @Nullable net.minecraft.world.entity.item.ItemEntity spawnAtLocation(ItemStack stack) { -+ if (!this.level.galeConfig().gameplayMechanics.fixes.mc123848) { ++ if (!this.level().galeConfig().gameplayMechanics.fixes.mc123848) { + return super.spawnAtLocation(stack); + } + return this.spawnAtLocation(stack, getDirection().equals(Direction.DOWN) ? -0.6F : 0.0F); diff --git a/patches/server/0073-Fix-cow-rotation-when-shearing-mooshroom.patch b/patches/server/0072-Fix-cow-rotation-when-shearing-mooshroom.patch similarity index 95% rename from patches/server/0073-Fix-cow-rotation-when-shearing-mooshroom.patch rename to patches/server/0072-Fix-cow-rotation-when-shearing-mooshroom.patch index 475ab10..8fd4c6a 100644 --- a/patches/server/0073-Fix-cow-rotation-when-shearing-mooshroom.patch +++ b/patches/server/0072-Fix-cow-rotation-when-shearing-mooshroom.patch @@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -index 68a5ee85e64802e4509ba0d184fc0ceb3cbe2d11..8ee1a0626e5a6c0ad19a25b8f476a2e12d69668d 100644 +index a04374f91f2fbb31219d86b6ae63bcf8fdf7318c..e3b92e8b2274ee6d07d1e9c74f669aeaab594919 100644 --- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java +++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java @@ -180,12 +180,21 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder extends Go +@@ -104,7 +104,10 @@ public class RangedBowAttackGoal extends Go this.strafingTime = 0; } @@ -52,14 +52,14 @@ index 6558b0d4bea99948fdc2b51751f3cfdc239d4b67..bedb5b8ee24817a494455f17e1f32b42 if (d > (double)(this.attackRadiusSqr * 0.75F)) { this.strafingBackwards = false; } else if (d < (double)(this.attackRadiusSqr * 0.25F)) { -@@ -111,7 +114,10 @@ public class RangedBowAttackGoal extends Go +@@ -119,7 +122,10 @@ public class RangedBowAttackGoal extends Go + } - this.mob.getMoveControl().strafe(this.strafingBackwards ? -0.5F : 0.5F, this.strafingClockwise ? 0.5F : -0.5F); this.mob.lookAt(livingEntity, 30.0F, 30.0F); - } else { + // Gale start - Purpur - fix MC-121706 + } -+ if (!hasStrafingTime || this.mob.level.galeConfig().gameplayMechanics.fixes.mc121706) { ++ if (!hasStrafingTime || this.mob.level().galeConfig().gameplayMechanics.fixes.mc121706) { + // Gale end - Purpur - fix MC-121706 this.mob.getLookControl().setLookAt(livingEntity, 30.0F, 30.0F); } diff --git a/patches/server/0075-Fix-MC-110386.patch b/patches/server/0074-Fix-MC-110386.patch similarity index 92% rename from patches/server/0075-Fix-MC-110386.patch rename to patches/server/0074-Fix-MC-110386.patch index 5cfbf09..70f71cd 100644 --- a/patches/server/0075-Fix-MC-110386.patch +++ b/patches/server/0074-Fix-MC-110386.patch @@ -19,7 +19,7 @@ the second will join in the fight against itself, causing it to attack itself repeatedly. See https://bugs.mojang.com/browse/MC-110386. diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java -index 39ea15c7577af186d93d4ad9a48034d746a86fc8..865eb39f4a1d2207c857acccc0695be92af2d6bb 100644 +index 80ef4b6649da3049f21624926fa38595d76c5da5..7ce47abc44e0dfe8ff90afeb25b14d8d98e6b1cc 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java +++ b/src/main/java/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java @@ -114,6 +114,11 @@ public class HurtByTargetGoal extends TargetGoal { @@ -27,7 +27,7 @@ index 39ea15c7577af186d93d4ad9a48034d746a86fc8..865eb39f4a1d2207c857acccc0695be9 protected void alertOther(Mob mob, LivingEntity target) { + // Gale start - Mirai - fix MC-110386 -+ if (mob == target && mob.level.galeConfig().gameplayMechanics.fixes.mc110386) { ++ if (mob == target && mob.level().galeConfig().gameplayMechanics.fixes.mc110386) { + return; + } + // Gale end - Mirai - fix MC-110386 diff --git a/patches/server/0076-Fix-MC-31819.patch b/patches/server/0075-Fix-MC-31819.patch similarity index 86% rename from patches/server/0076-Fix-MC-31819.patch rename to patches/server/0075-Fix-MC-31819.patch index 3a2c895..d9c2a91 100644 --- a/patches/server/0076-Fix-MC-31819.patch +++ b/patches/server/0075-Fix-MC-31819.patch @@ -19,21 +19,21 @@ the displayed hunger bar never goes down. Hunger (or any related value, includin should not go down on peaceful. See https://bugs.mojang.com/browse/MC-31819. diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 0629c471d38a77c44fc1c86ccdfcb0690f61ca17..65e356449e9c96c937b45eaab94aa6cc4296fcb9 100644 +index 58152160d609d0e9d105153aeb166a56a7955603..6a906b1f37e52472527e9f19e965be9f155dd404 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1902,6 +1902,11 @@ public abstract class Player extends LivingEntity { +@@ -1929,6 +1929,11 @@ public abstract class Player extends LivingEntity { } public void causeFoodExhaustion(float f, EntityExhaustionEvent.ExhaustionReason reason) { + // Gale start - Mirai - fix MC-31819 -+ if (this.level.galeConfig().gameplayMechanics.fixes.mc31819 && this.level.getDifficulty() == Difficulty.PEACEFUL) { ++ if (this.level().galeConfig().gameplayMechanics.fixes.mc31819 && this.level().getDifficulty() == Difficulty.PEACEFUL) { + return; + } + // Gale end - Mirai - fix MC-31819 // CraftBukkit end if (!this.abilities.invulnerable) { - if (!this.level.isClientSide) { + if (!this.level().isClientSide) { diff --git a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java index 899aa60e2c20c4d973777d4f82f524e2fb902c93..aab67ad8212bdc5a8afe9b1a9634bde3f78eb496 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java diff --git a/patches/server/0077-Fix-MC-26304.patch b/patches/server/0076-Fix-MC-26304.patch similarity index 100% rename from patches/server/0077-Fix-MC-26304.patch rename to patches/server/0076-Fix-MC-26304.patch diff --git a/patches/server/0078-End-gateway-should-check-if-entity-can-use-portal.patch b/patches/server/0077-End-gateway-should-check-if-entity-can-use-portal.patch similarity index 89% rename from patches/server/0078-End-gateway-should-check-if-entity-can-use-portal.patch rename to patches/server/0077-End-gateway-should-check-if-entity-can-use-portal.patch index 0e62dbe..378b0c2 100644 --- a/patches/server/0078-End-gateway-should-check-if-entity-can-use-portal.patch +++ b/patches/server/0077-End-gateway-should-check-if-entity-can-use-portal.patch @@ -37,14 +37,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java -index c73024cc62490c336ffe26313580e88d25ca7078..c86dea3e38d72929a4970334e3cc4e93e353c598 100644 +index d9baa85962236c42219cf09d4f3129be93ff069c..414a47a82927bed02a4f3cf330c78e46f1d66782 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java @@ -178,6 +178,7 @@ public class TheEndGatewayBlockEntity extends TheEndPortalBlockEntity { public static void teleportEntity(Level world, BlockPos pos, BlockState state, Entity entity, TheEndGatewayBlockEntity blockEntity) { if (world instanceof ServerLevel && !blockEntity.isCoolingDown()) { -+ if (entity.level.galeConfig().gameplayMechanics.fixes.checkCanChangeDimensionsBeforeUseEndGateway && world.galeConfig().gameplayMechanics.fixes.checkCanChangeDimensionsBeforeUseEndGateway && !entity.canChangeDimensions()) return; // Gale - Purpur - end gateway should check if entity can use portal ++ if (entity.level().galeConfig().gameplayMechanics.fixes.checkCanChangeDimensionsBeforeUseEndGateway && world.galeConfig().gameplayMechanics.fixes.checkCanChangeDimensionsBeforeUseEndGateway && !entity.canChangeDimensions()) return; // Gale - Purpur - end gateway should check if entity can use portal ServerLevel worldserver = (ServerLevel) world; blockEntity.teleportCooldown = 100; diff --git a/patches/server/0079-Prevent-entities-eating-blocks-in-non-ticking-chunks.patch b/patches/server/0078-Prevent-entities-eating-blocks-in-non-ticking-chunks.patch similarity index 90% rename from patches/server/0079-Prevent-entities-eating-blocks-in-non-ticking-chunks.patch rename to patches/server/0078-Prevent-entities-eating-blocks-in-non-ticking-chunks.patch index 67f512b..a229d80 100644 --- a/patches/server/0079-Prevent-entities-eating-blocks-in-non-ticking-chunks.patch +++ b/patches/server/0078-Prevent-entities-eating-blocks-in-non-ticking-chunks.patch @@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java -index 80aa539f7c6a6ee44338de084cdcdf5fb4ef996a..cb8fc8a88c14d2374a0bbe35aa1c2056d625b71c 100644 +index abf796c3369da6b73c8587dfc05f56d0b8933fde..0fe194854a60a8d09f2a793a34132f55cb338713 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java +++ b/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java @@ -31,10 +31,16 @@ public class EatBlockGoal extends Goal { @@ -46,8 +46,8 @@ index 80aa539f7c6a6ee44338de084cdcdf5fb4ef996a..cb8fc8a88c14d2374a0bbe35aa1c2056 public boolean canUse() { + // Gale start - Purpur - prevent entities eating blocks in non-ticking chunks + BlockPos blockposition = this.mob.blockPosition; -+ net.minecraft.world.level.chunk.LevelChunk chunk = this.mob.level.getChunkIfLoaded(blockposition); -+ if (this.mob.level.galeConfig().gameplayMechanics.entitiesCanEatBlocksInNonTickingChunks && (chunk == null || chunk.playerChunk == null || !((net.minecraft.server.level.ServerLevel) this.mob.level).isPositionEntityTicking(blockposition))) { ++ net.minecraft.world.level.chunk.LevelChunk chunk = this.mob.level().getChunkIfLoaded(blockposition); ++ if (this.mob.level().galeConfig().gameplayMechanics.entitiesCanEatBlocksInNonTickingChunks && (chunk == null || chunk.playerChunk == null || !((net.minecraft.server.level.ServerLevel) this.mob.level()).isPositionEntityTicking(blockposition))) { + return false; + } + // Gale end - Purpur - prevent entities eating blocks in non-ticking chunks diff --git a/patches/server/0080-Make-arrow-movement-resetting-despawn-counter-config.patch b/patches/server/0079-Make-arrow-movement-resetting-despawn-counter-config.patch similarity index 92% rename from patches/server/0080-Make-arrow-movement-resetting-despawn-counter-config.patch rename to patches/server/0079-Make-arrow-movement-resetting-despawn-counter-config.patch index a8c159e..6dacf49 100644 --- a/patches/server/0080-Make-arrow-movement-resetting-despawn-counter-config.patch +++ b/patches/server/0079-Make-arrow-movement-resetting-despawn-counter-config.patch @@ -42,7 +42,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -index 5d6d26cfe8f0ab68a3145214b3fc126ca7a71a66..e190f414ab5af7326d92af3fddf3f7a2c3358fa0 100644 +index 7226be19248a1ffb8ff2c89b55882529d33a6c0c..cff4d09af11d1741bf3301b457555d71e77e801c 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java +++ b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java @@ -312,7 +312,7 @@ public abstract class AbstractArrow extends Projectile { @@ -50,7 +50,7 @@ index 5d6d26cfe8f0ab68a3145214b3fc126ca7a71a66..e190f414ab5af7326d92af3fddf3f7a2 this.setDeltaMovement(vec3d.multiply((double) (this.random.nextFloat() * 0.2F), (double) (this.random.nextFloat() * 0.2F), (double) (this.random.nextFloat() * 0.2F))); - this.life = 0; -+ if (this.level.galeConfig().gameplayMechanics.arrowMovementResetsDespawnCounter) this.life = 0; // Gale - Purpur - make arrow movement resetting despawn counter configurable ++ if (this.level().galeConfig().gameplayMechanics.arrowMovementResetsDespawnCounter) this.life = 0; // Gale - Purpur - make arrow movement resetting despawn counter configurable } @Override diff --git a/patches/server/0081-Make-logging-login-locations-configurable.patch b/patches/server/0080-Make-logging-login-locations-configurable.patch similarity index 94% rename from patches/server/0081-Make-logging-login-locations-configurable.patch rename to patches/server/0080-Make-logging-login-locations-configurable.patch index b00b713..096f0ad 100644 --- a/patches/server/0081-Make-logging-login-locations-configurable.patch +++ b/patches/server/0080-Make-logging-login-locations-configurable.patch @@ -13,10 +13,10 @@ As part of: JettPack (https://gitlab.com/Titaniumtown/JettPack) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 31554b46b0e6c84d8b480b10e1c46649d2518488..69d22ca2f2a2b4d85d72eeef36a3843a11ee4c91 100644 +index efc958959b772312acfbcfb0cf52c6aa6be77fb2..6deac057bae3fe80bbdd39fc6151ee66e754c71e 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -473,7 +473,13 @@ public abstract class PlayerList { +@@ -480,7 +480,13 @@ public abstract class PlayerList { } // Paper end // CraftBukkit - Moved from above, added world diff --git a/patches/server/0082-Reduce-array-allocations.patch b/patches/server/0081-Reduce-array-allocations.patch similarity index 93% rename from patches/server/0082-Reduce-array-allocations.patch rename to patches/server/0081-Reduce-array-allocations.patch index b5e5b0c..f4c740e 100644 --- a/patches/server/0082-Reduce-array-allocations.patch +++ b/patches/server/0081-Reduce-array-allocations.patch @@ -81,7 +81,7 @@ index ae60bd96b5284d54676d8e7e4dd5d170b526ec1e..b269b9b55c10542b16af301be3f43798 return true; } diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java -index f597d65d56964297eeeed6c7e77703764178fee0..d503c0a7c4706af28a7db2face5efd8d595831d1 100644 +index ce449b7b6f615f2c8240e4207f06d4e54ae0083e..c21398bd39bd1e96f8005539909aa7497fb19c9e 100644 --- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java @@ -2,9 +2,9 @@ package io.papermc.paper.world; @@ -94,8 +94,8 @@ index f597d65d56964297eeeed6c7e77703764178fee0..d503c0a7c4706af28a7db2face5efd8d +import me.titaniumtown.ArrayConstants; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ChunkHolder; - import net.minecraft.server.level.ServerLevel; -@@ -81,7 +81,7 @@ public final class ChunkEntitySlices { + import net.minecraft.server.level.FullChunkStatus; +@@ -82,7 +82,7 @@ public final class ChunkEntitySlices { } } @@ -104,7 +104,7 @@ index f597d65d56964297eeeed6c7e77703764178fee0..d503c0a7c4706af28a7db2face5efd8d } public CompoundTag save() { -@@ -298,7 +298,7 @@ public final class ChunkEntitySlices { +@@ -299,7 +299,7 @@ public final class ChunkEntitySlices { protected static final class BasicEntityList { @@ -113,7 +113,7 @@ index f597d65d56964297eeeed6c7e77703764178fee0..d503c0a7c4706af28a7db2face5efd8d protected static final int DEFAULT_CAPACITY = 4; protected E[] storage; -@@ -309,7 +309,7 @@ public final class ChunkEntitySlices { +@@ -310,7 +310,7 @@ public final class ChunkEntitySlices { } public BasicEntityList(final int cap) { @@ -122,7 +122,7 @@ index f597d65d56964297eeeed6c7e77703764178fee0..d503c0a7c4706af28a7db2face5efd8d } public boolean isEmpty() { -@@ -321,7 +321,7 @@ public final class ChunkEntitySlices { +@@ -322,7 +322,7 @@ public final class ChunkEntitySlices { } private void resize() { @@ -271,7 +271,7 @@ index 25ad2c6ff968f4a6b16b4dea3f67341a4261f2a4..5d95b7164bb576e1722fc2498e5e4966 @Override diff --git a/src/main/java/net/minecraft/nbt/ListTag.java b/src/main/java/net/minecraft/nbt/ListTag.java -index 749d3e67a877d7d1ed47b5fef511a604ee6589b6..54ea449b87e012332a3a99807d7ab8afbd00e8de 100644 +index b74b77ec2fd45c7a44e4f2696c4f57075c3d4080..beb1f8a3ac601b14a6e7da27ea8c840ce51456c1 100644 --- a/src/main/java/net/minecraft/nbt/ListTag.java +++ b/src/main/java/net/minecraft/nbt/ListTag.java @@ -2,6 +2,8 @@ package net.minecraft.nbt; @@ -346,7 +346,7 @@ index a2920b8a9eff77d9c5d1d7f70ad3abdacba8f0fa..43f402d9032e4570a81a80e412215598 protected CipherBase(Cipher cipher) { this.cipher = cipher; diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java -index f9e10bf048929886db3c414038d2c7e9f84226a6..0aef4fe2af72e8006f37c02f4f8eaa651b870671 100644 +index c45c1d519aba414557bf1a4c9260f928bd8d9b14..e152e851db27ea4e43678b91a65ce303cb7004b3 100644 --- a/src/main/java/net/minecraft/network/Connection.java +++ b/src/main/java/net/minecraft/network/Connection.java @@ -21,7 +21,6 @@ import io.netty.channel.epoll.EpollSocketChannel; @@ -367,7 +367,7 @@ index f9e10bf048929886db3c414038d2c7e9f84226a6..0aef4fe2af72e8006f37c02f4f8eaa65 import net.minecraft.Util; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; -@@ -318,7 +318,7 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -316,7 +316,7 @@ public class Connection extends SimpleChannelInboundHandler> { } public void setListener(PacketListener listener) { @@ -399,7 +399,7 @@ index 8a68baf6bd46b59cf57c94ffe5651d47a7cae99c..bd7fa1b2a6fed82ce66fe2b8e3498e4d private static final FormattedText TEXT_NULL = FormattedText.of("null"); private final String key; diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java -index cdbf10339a8e8846a8c364de483a0ccd95cd225a..95462967b057e00b0616857e3c4eb05e5840ba3d 100644 +index dae36c6452ccd57a436dd918547b64d59957ab0a..f06cafd80d7c027ca97e50bcb45ad520d17c7e55 100644 --- a/src/main/java/net/minecraft/server/Main.java +++ b/src/main/java/net/minecraft/server/Main.java @@ -93,7 +93,7 @@ public class Main { @@ -412,7 +412,7 @@ index cdbf10339a8e8846a8c364de483a0ccd95cd225a..95462967b057e00b0616857e3c4eb05e OptionSpec optionspec11 = optionparser.accepts("port").withRequiredArg().ofType(Integer.class).defaultsTo(-1, new Integer[0]); OptionSpec optionspec12 = optionparser.accepts("serverId").withRequiredArg(); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index f53370e581b8d07d993323be5df4d737d1e13794..71b4de55621ef7bb2df4fbfc611ce32023cdd628 100644 +index a6d5dd36d297c976ad3302ae7c3c2080c978f68d..850f2299595e081cbac459304e8c52d94d90f6f4 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -50,6 +50,8 @@ import java.util.stream.Collectors; @@ -425,10 +425,10 @@ index f53370e581b8d07d993323be5df4d737d1e13794..71b4de55621ef7bb2df4fbfc611ce320 import net.minecraft.ReportedException; import net.minecraft.SharedConstants; diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index 48adbfbb8a55f6719c92a1fe83c64d87f1b236d5..834b497053f4fa20cf94c00e1ee2db4838bdf233 100644 +index 1717844256fe6479e3d7125db3937354578d17d0..0ced61876ad4790c0a0c8e24be721241fa7fd319 100644 --- a/src/main/java/net/minecraft/server/level/ServerEntity.java +++ b/src/main/java/net/minecraft/server/level/ServerEntity.java -@@ -338,7 +338,7 @@ public class ServerEntity { +@@ -332,7 +332,7 @@ public class ServerEntity { if (this.entity instanceof LivingEntity) { List> list = Lists.newArrayList(); @@ -438,7 +438,7 @@ index 48adbfbb8a55f6719c92a1fe83c64d87f1b236d5..834b497053f4fa20cf94c00e1ee2db48 for (int j = 0; j < i; ++j) { diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 9676d4523c14559cd13e6c53d220bf7472558d8a..288372fbf6c1e669ee16381be57120e5b569f4a4 100644 +index efbc8483a51c5272ca81747c82dc65e7f0f5c232..9879599c38db4f3332c347b6b0c26034bc7749bc 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -38,6 +38,8 @@ import java.util.stream.Collectors; @@ -450,7 +450,7 @@ index 9676d4523c14559cd13e6c53d220bf7472558d8a..288372fbf6c1e669ee16381be57120e5 import net.minecraft.CrashReport; import net.minecraft.Util; import net.minecraft.core.BlockPos; -@@ -898,7 +900,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1040,7 +1042,7 @@ public class ServerLevel extends Level implements WorldGenLevel { BlockPos blockposition2 = blockposition.set(j + randomX, randomY, k + randomZ); BlockState iblockdata = com.destroystokyo.paper.util.maplist.IBlockDataList.getBlockDataFromRaw(raw); @@ -459,7 +459,7 @@ index 9676d4523c14559cd13e6c53d220bf7472558d8a..288372fbf6c1e669ee16381be57120e5 // We drop the fluid tick since LAVA is ALREADY TICKED by the above method (See LiquidBlock). // TODO CHECK ON UPDATE (ping the Canadian) } -@@ -1152,7 +1154,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1294,7 +1296,7 @@ public class ServerLevel extends Level implements WorldGenLevel { public static List getCurrentlyTickingEntities() { Entity ticking = currentlyTickingEntity.get(); @@ -469,7 +469,7 @@ index 9676d4523c14559cd13e6c53d220bf7472558d8a..288372fbf6c1e669ee16381be57120e5 return ret; } diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index a63236fb2995e8eea9185a19417dd6464bb832a0..9f2f019ad39b0aa16ebe0eabd79fc5beba897959 100644 +index fcf395497593704858439f7f9ca8b46b699c42df..574c8a2680c66d9e8bc14b1128652f1f4d16210b 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -29,6 +29,8 @@ import java.util.function.UnaryOperator; @@ -515,8 +515,8 @@ index a63236fb2995e8eea9185a19417dd6464bb832a0..9f2f019ad39b0aa16ebe0eabd79fc5be } } else { if (elapsedTime >= 15000L) { // 15 seconds -@@ -877,13 +875,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - // PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); // Paper - run this async +@@ -884,13 +882,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + // PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // Paper - run this async // CraftBukkit start if (this.chatSpamTickCount.addAndGet(io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.tabSpamIncrement) > io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.tabSpamLimit && !this.server.getPlayerList().isOp(this.player.getGameProfile())) { // Paper start - split and make configurable - server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause @@ -526,17 +526,17 @@ index a63236fb2995e8eea9185a19417dd6464bb832a0..9f2f019ad39b0aa16ebe0eabd79fc5be // Paper start String str = packet.getCommand(); int index = -1; if (str.length() > 64 && ((index = str.indexOf(' ')) == -1 || index >= 64)) { -- server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause +- server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper + server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause // Gale - JettPack - reduce array allocations return; } // Paper end -@@ -3301,7 +3299,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic +@@ -3300,7 +3298,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic // Paper start if (!org.bukkit.Bukkit.isPrimaryThread()) { - if (recipeSpamPackets.addAndGet(io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.recipeSpamIncrement) > io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.recipeSpamLimit) { -- server.scheduleOnMain(() -> this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause -+ server.scheduleOnMain(() -> this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause // Gale - JettPack - reduce array allocations + if (this.recipeSpamPackets.addAndGet(io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.recipeSpamIncrement) > io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.recipeSpamLimit) { +- this.server.scheduleOnMain(() -> this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam", new Object[0]), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause ++ this.server.scheduleOnMain(() -> this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause // Gale - JettPack - reduce array allocations return; } } @@ -578,7 +578,7 @@ index 2ff578e4a953ffcf5176815ba8e3f06f73499989..a436ef49325c9cae1008d5763373cce8 final String s; diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 69d22ca2f2a2b4d85d72eeef36a3843a11ee4c91..b03789c629c87d524e24a6e3bb893ae2bac1d5c1 100644 +index 6deac057bae3fe80bbdd39fc6151ee66e754c71e..ddde03a81d7a39188ac26c16658301e138356453 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -27,6 +27,8 @@ import java.util.UUID; @@ -590,7 +590,7 @@ index 69d22ca2f2a2b4d85d72eeef36a3843a11ee4c91..b03789c629c87d524e24a6e3bb893ae2 import net.minecraft.ChatFormatting; import net.minecraft.FileUtil; import net.minecraft.commands.CommandSourceStack; -@@ -701,7 +703,7 @@ public abstract class PlayerList { +@@ -708,7 +710,7 @@ public abstract class PlayerList { while (iterator.hasNext()) { entityplayer = (ServerPlayer) iterator.next(); this.save(entityplayer); // CraftBukkit - Force the player's inventory to be saved @@ -649,7 +649,7 @@ index 0ee04fe6ff6a4d09754f326526ae04fe7226bab2..a4f7fee3ea112c8f7b0b94949f9eb899 } } diff --git a/src/main/java/net/minecraft/util/ZeroBitStorage.java b/src/main/java/net/minecraft/util/ZeroBitStorage.java -index 5d8e9bdf5538b19681f21949368d862fab8a89ad..75ca7ae6028e971f73988f5e71598696c0765cc8 100644 +index 311625277a26c9c187025a1036978229241b965f..79b223ef3c86f3bc035e9abd84aa080b5d722f61 100644 --- a/src/main/java/net/minecraft/util/ZeroBitStorage.java +++ b/src/main/java/net/minecraft/util/ZeroBitStorage.java @@ -2,10 +2,11 @@ package net.minecraft.util; @@ -679,10 +679,10 @@ index 97ff19efa0b3943ccb7a6e02cba6ed2fea61adac..b2ae2bd8bd4ff3cb6457e8c08172e348 private EquipmentSlot(EquipmentSlot.Type type, int entityId, int armorStandId, String name) { this.type = type; diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 848022c049e27d80ceb11f0cee6cbda15edf2ff3..8e739f81455ef41502b195c360d2e4243c29cda4 100644 +index 1040e76c829521b214e5a96fb41ee60e223641d3..1016a70c2b468463cb74aa6bbfe3aa65eb297786 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -3068,7 +3068,7 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -3106,7 +3106,7 @@ public abstract class LivingEntity extends Entity implements Attackable { @Nullable private Map collectEquipmentChanges() { Map map = null; @@ -692,10 +692,10 @@ index 848022c049e27d80ceb11f0cee6cbda15edf2ff3..8e739f81455ef41502b195c360d2e424 for (int j = 0; j < i; ++j) { diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index a19956dbbb43f64043381bd968af74550b8a359a..728ecec837e9d9051bcf3533d63d5f179d34c81c 100644 +index 3bc50cc77bc8723dd607134f795aae55784aa657..fa47779f1007325ee77f2b43195bf1892a2d7d65 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -1044,7 +1044,7 @@ public abstract class Mob extends LivingEntity implements Targeting { +@@ -1047,7 +1047,7 @@ public abstract class Mob extends LivingEntity implements Targeting { @Override protected void dropCustomDeathLoot(DamageSource source, int lootingMultiplier, boolean allowDrops) { super.dropCustomDeathLoot(source, lootingMultiplier, allowDrops); @@ -704,7 +704,7 @@ index a19956dbbb43f64043381bd968af74550b8a359a..728ecec837e9d9051bcf3533d63d5f17 int j = aenumitemslot.length; for (int k = 0; k < j; ++k) { -@@ -1106,7 +1106,7 @@ public abstract class Mob extends LivingEntity implements Targeting { +@@ -1109,7 +1109,7 @@ public abstract class Mob extends LivingEntity implements Targeting { } boolean flag = true; @@ -713,7 +713,7 @@ index a19956dbbb43f64043381bd968af74550b8a359a..728ecec837e9d9051bcf3533d63d5f17 int j = aenumitemslot.length; for (int k = 0; k < j; ++k) { -@@ -1193,7 +1193,7 @@ public abstract class Mob extends LivingEntity implements Targeting { +@@ -1196,7 +1196,7 @@ public abstract class Mob extends LivingEntity implements Targeting { float f = localDifficulty.getSpecialMultiplier(); this.enchantSpawnedWeapon(random, f); @@ -722,7 +722,7 @@ index a19956dbbb43f64043381bd968af74550b8a359a..728ecec837e9d9051bcf3533d63d5f17 int i = aenumitemslot.length; for (int j = 0; j < i; ++j) { -@@ -1412,7 +1412,7 @@ public abstract class Mob extends LivingEntity implements Targeting { +@@ -1415,7 +1415,7 @@ public abstract class Mob extends LivingEntity implements Targeting { t0.setInvulnerable(this.isInvulnerable()); if (flag) { t0.setCanPickUpLoot(this.canPickUpLoot()); @@ -732,7 +732,7 @@ index a19956dbbb43f64043381bd968af74550b8a359a..728ecec837e9d9051bcf3533d63d5f17 for (int j = 0; j < i; ++j) { diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -index 71a36cf9b976443cca9ab63cd0eb23253f638562..201b0e1b25d0773bbcf9c1ed69fd888a61c6a16f 100644 +index 25ed5571b24e590bc95056020d84496492b53298..087acc992a83b2a7e40a0e24f00e283cc139ce59 100644 --- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java +++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java @@ -234,7 +234,7 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { @@ -745,10 +745,10 @@ index 71a36cf9b976443cca9ab63cd0eb23253f638562..201b0e1b25d0773bbcf9c1ed69fd888a for (int j = 0; j < i; ++j) { diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index d81fcbadc5c0b3d4b54dde5d47a0f847d8ec6918..84067c11ee5dd18195afa21dec9d80adb9815866 100644 +index b367ec1feaccbd67e9e28c0d0515e163f37135fe..7ca7834a1a0593a6d1eb105e215784100076d421 100644 --- a/src/main/java/net/minecraft/world/item/ItemStack.java +++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -1008,7 +1008,7 @@ public final class ItemStack { +@@ -1014,7 +1014,7 @@ public final class ItemStack { int k; if (ItemStack.shouldShowInTooltip(i, ItemStack.TooltipPart.MODIFIERS)) { @@ -758,7 +758,7 @@ index d81fcbadc5c0b3d4b54dde5d47a0f847d8ec6918..84067c11ee5dd18195afa21dec9d80ad k = aenumitemslot.length; diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java -index 117a376f99b1d26c5ffa64c8551ac1e666b15888..e67124e18aec0ac0bd1f21faa73a6259fd33e40c 100644 +index 9c1285e31d947f92e0b00149e342e793898e0d7c..dccb73d1cf06da3e3180992ea6edce322f4316ee 100644 --- a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java +++ b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java @@ -12,6 +12,8 @@ import java.util.Iterator; @@ -797,7 +797,7 @@ index 2bfbdaeb2b0d99dfd956cd5936403fe8b0eeae64..84f1c4c3ded4f201899f3c74e639349b public Enchantments() {} diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index d4527b0f11893925c93b1846305fb3b17ba8e89a..b19e842be160748a6969e498952eb02ffece2ecc 100644 +index b9431be4e6fb366bbdb7e66c9570f26a37379488..4b8736f19c0b968fac27037d2d0eb3f6d3a2fe94 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -12,6 +12,8 @@ import java.util.function.Consumer; @@ -809,7 +809,7 @@ index d4527b0f11893925c93b1846305fb3b17ba8e89a..b19e842be160748a6969e498952eb02f import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.core.BlockPos; -@@ -1581,7 +1583,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1508,7 +1510,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public org.bukkit.entity.Entity[] getChunkEntities(int chunkX, int chunkZ) { io.papermc.paper.world.ChunkEntitySlices slices = ((ServerLevel)this).getEntityLookup().getChunk(chunkX, chunkZ); if (slices == null) { @@ -819,7 +819,7 @@ index d4527b0f11893925c93b1846305fb3b17ba8e89a..b19e842be160748a6969e498952eb02f return slices.getChunkEntities(); } diff --git a/src/main/java/net/minecraft/world/level/block/ComposterBlock.java b/src/main/java/net/minecraft/world/level/block/ComposterBlock.java -index 6fab2b69a0af298bd00b309efcd6aa8399e23d1f..29aa7303ce5854a7f2b5ed5f9ff02ca7e00bf6c0 100644 +index 87153b5576fed05103183a9860d804c2c8cfbe1c..8d741018c7dc9a2ac1b666025a4e041010a745d4 100644 --- a/src/main/java/net/minecraft/world/level/block/ComposterBlock.java +++ b/src/main/java/net/minecraft/world/level/block/ComposterBlock.java @@ -3,6 +3,8 @@ package net.minecraft.world.level.block; @@ -831,7 +831,7 @@ index 6fab2b69a0af298bd00b309efcd6aa8399e23d1f..29aa7303ce5854a7f2b5ed5f9ff02ca7 import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -@@ -414,7 +416,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { +@@ -416,7 +418,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { @Override public int[] getSlotsForFace(Direction side) { @@ -840,7 +840,7 @@ index 6fab2b69a0af298bd00b309efcd6aa8399e23d1f..29aa7303ce5854a7f2b5ed5f9ff02ca7 } @Override -@@ -463,7 +465,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { +@@ -465,7 +467,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { @Override public int[] getSlotsForFace(Direction side) { @@ -849,7 +849,7 @@ index 6fab2b69a0af298bd00b309efcd6aa8399e23d1f..29aa7303ce5854a7f2b5ed5f9ff02ca7 } @Override -@@ -505,7 +507,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { +@@ -507,7 +509,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { @Override public int[] getSlotsForFace(Direction side) { @@ -859,7 +859,7 @@ index 6fab2b69a0af298bd00b309efcd6aa8399e23d1f..29aa7303ce5854a7f2b5ed5f9ff02ca7 @Override diff --git a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java -index a3f073066f6e2eea8964461ad2b0409ade202f35..13f9226d4f8f591cd0095fda2f68bfad8e89b2d4 100644 +index 448fa4f4f200430d6ce3051763c7ceb697696146..381818dddc00be81d81456161fe6c6a29603987c 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java @@ -9,6 +9,8 @@ import java.util.Iterator; diff --git a/patches/server/0083-Optimize-sun-burn-tick.patch b/patches/server/0082-Optimize-sun-burn-tick.patch similarity index 74% rename from patches/server/0083-Optimize-sun-burn-tick.patch rename to patches/server/0082-Optimize-sun-burn-tick.patch index 041c706..566146f 100644 --- a/patches/server/0083-Optimize-sun-burn-tick.patch +++ b/patches/server/0082-Optimize-sun-burn-tick.patch @@ -13,10 +13,10 @@ As part of: JettPack (https://gitlab.com/Titaniumtown/JettPack) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 254feb89dc2bdb7c994fb65b1a345f879ec8ff72..6990b9cf582f32a9ec259f5641eb1d58ffff2a9d 100644 +index eb395d6498f3f43c20d371efd8e1f410400f1f2a..220880d8a723724174fe948765e108ecaf24872b 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -291,7 +291,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -293,7 +293,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { public double xo; public double yo; public double zo; @@ -25,19 +25,19 @@ index 254feb89dc2bdb7c994fb65b1a345f879ec8ff72..6990b9cf582f32a9ec259f5641eb1d58 public BlockPos blockPosition; // Gale - Pufferfish - optimize entity coordinate key - private -> public private ChunkPos chunkPosition; private Vec3 deltaMovement; -@@ -1874,9 +1874,17 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -1993,9 +1993,17 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { /** @deprecated */ @Deprecated public float getLightLevelDependentMagicValue() { -- return this.level.hasChunkAt(this.getBlockX(), this.getBlockZ()) ? this.level.getLightLevelDependentMagicValue(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())) : 0.0F; -+ return this.level.getLightLevelDependentMagicValue(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())); // Gale - JettPack - optimize sun burn tick - allow passing BlockPos to getLightLevelDependentMagicValue +- return this.level().hasChunkAt(this.getBlockX(), this.getBlockZ()) ? this.level().getLightLevelDependentMagicValue(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())) : 0.0F; ++ return this.getLightLevelDependentMagicValue(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())); // Gale - JettPack - optimize sun burn tick - allow passing BlockPos to getLightLevelDependentMagicValue } + // Gale start - JettPack - optimize sun burn tick - allow passing BlockPos to getLightLevelDependentMagicValue + /** @deprecated */ + @Deprecated + public float getLightLevelDependentMagicValue(BlockPos pos) { -+ return this.level.hasChunkAt(this.getBlockX(), this.getBlockZ()) ? this.level.getLightLevelDependentMagicValue(pos) : 0.0F; ++ return this.level().hasChunkAt(this.getBlockX(), this.getBlockZ()) ? this.level.getLightLevelDependentMagicValue(pos) : 0.0F; + } + // Gale end - JettPack - optimize sun burn tick - allow passing BlockPos to getLightLevelDependentMagicValue + @@ -45,10 +45,10 @@ index 254feb89dc2bdb7c994fb65b1a345f879ec8ff72..6990b9cf582f32a9ec259f5641eb1d58 this.absMoveTo(x, y, z); this.setYRot(yaw % 360.0F); diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 728ecec837e9d9051bcf3533d63d5f179d34c81c..f2eca869a9301c8e6536396f55fd5dc871a3dfbc 100644 +index fa47779f1007325ee77f2b43195bf1892a2d7d65..035a0a8620fc46dbf026c65ccf2542d9f49e22b0 100644 --- a/src/main/java/net/minecraft/world/entity/Mob.java +++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -1669,13 +1669,29 @@ public abstract class Mob extends LivingEntity implements Targeting { +@@ -1671,13 +1671,29 @@ public abstract class Mob extends LivingEntity implements Targeting { } @@ -58,7 +58,7 @@ index 728ecec837e9d9051bcf3533d63d5f179d34c81c..f2eca869a9301c8e6536396f55fd5dc8 + // Gale end - JettPack - optimize sun burn tick - cache eye blockpos + public boolean isSunBurnTick() { - if (this.level.isDay() && !this.level.isClientSide) { + if (this.level().isDay() && !this.level().isClientSide) { - float f = this.getLightLevelDependentMagicValue(); - BlockPos blockposition = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ()); + // Gale start - JettPack - optimize sun burn tick - optimizations and cache eye blockpos @@ -76,8 +76,8 @@ index 728ecec837e9d9051bcf3533d63d5f179d34c81c..f2eca869a9301c8e6536396f55fd5dc8 + // Gale end - JettPack - optimize sun burn tick - optimizations and cache eye blockpos boolean flag = this.isInWaterRainOrBubble() || this.isInPowderSnow || this.wasInPowderSnow; -- if (f > 0.5F && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && !flag && this.level.canSeeSky(blockposition)) { -+ if (!flag && this.level.canSeeSky(this.cached_eye_blockpos)) { // Gale - JettPack - optimize sun burn tick - optimizations and cache eye blockpos +- if (f > 0.5F && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && !flag && this.level().canSeeSky(blockposition)) { ++ if (!flag && this.level().canSeeSky(this.cached_eye_blockpos)) { // Gale - JettPack - optimize sun burn tick - optimizations and cache eye blockpos return true; } } diff --git a/patches/server/0084-Reduce-lambda-and-Optional-allocation-in-EntityBased.patch b/patches/server/0083-Reduce-lambda-and-Optional-allocation-in-EntityBased.patch similarity index 100% rename from patches/server/0084-Reduce-lambda-and-Optional-allocation-in-EntityBased.patch rename to patches/server/0083-Reduce-lambda-and-Optional-allocation-in-EntityBased.patch diff --git a/patches/server/0085-Replace-AI-goal-set-with-optimized-collection.patch b/patches/server/0084-Replace-AI-goal-set-with-optimized-collection.patch similarity index 100% rename from patches/server/0085-Replace-AI-goal-set-with-optimized-collection.patch rename to patches/server/0084-Replace-AI-goal-set-with-optimized-collection.patch diff --git a/patches/server/0086-Replace-game-rules-map-with-optimized-collection.patch b/patches/server/0085-Replace-game-rules-map-with-optimized-collection.patch similarity index 100% rename from patches/server/0086-Replace-game-rules-map-with-optimized-collection.patch rename to patches/server/0085-Replace-game-rules-map-with-optimized-collection.patch diff --git a/patches/server/0087-Replace-AI-attributes-with-optimized-collections.patch b/patches/server/0086-Replace-AI-attributes-with-optimized-collections.patch similarity index 100% rename from patches/server/0087-Replace-AI-attributes-with-optimized-collections.patch rename to patches/server/0086-Replace-AI-attributes-with-optimized-collections.patch diff --git a/patches/server/0088-Replace-class-map-with-optimized-collection.patch b/patches/server/0087-Replace-class-map-with-optimized-collection.patch similarity index 100% rename from patches/server/0088-Replace-class-map-with-optimized-collection.patch rename to patches/server/0087-Replace-class-map-with-optimized-collection.patch diff --git a/patches/server/0089-Replace-throttle-tracker-map-with-optimized-collecti.patch b/patches/server/0088-Replace-throttle-tracker-map-with-optimized-collecti.patch similarity index 97% rename from patches/server/0089-Replace-throttle-tracker-map-with-optimized-collecti.patch rename to patches/server/0088-Replace-throttle-tracker-map-with-optimized-collecti.patch index 171d726..e8a85ed 100644 --- a/patches/server/0089-Replace-throttle-tracker-map-with-optimized-collecti.patch +++ b/patches/server/0088-Replace-throttle-tracker-map-with-optimized-collecti.patch @@ -33,7 +33,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRA IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java -index 8f52acc03943b18f87b3b2581066496a254b1156..f3bbfb472b9be083dd1f1a317b90dda5c3c4f851 100644 +index 63cf71940f6480c593a43bd39900c50676367404..2821de09a36fc315897129f4691ba713386737db 100644 --- a/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java @@ -1,10 +1,11 @@ @@ -67,7 +67,7 @@ index 8f52acc03943b18f87b3b2581066496a254b1156..f3bbfb472b9be083dd1f1a317b90dda5 + private static final Object2LongOpenHashMap throttleTracker = new Object2LongOpenHashMap<>(); // Gale - Dionysus - replace throttle tracker map with optimized collection private static int throttleCounter = 0; // CraftBukkit end - private static final Component IGNORE_STATUS_REASON = Component.literal("Ignoring status request"); + private static final Component IGNORE_STATUS_REASON = Component.translatable("disconnect.ignoring_status_request"); @@ -51,7 +53,7 @@ public class ServerHandshakePacketListenerImpl implements ServerHandshakePacketL InetAddress address = ((java.net.InetSocketAddress) this.connection.getRemoteAddress()).getAddress(); diff --git a/patches/server/0090-Replace-shape-full-block-cache-with-hashtable.patch b/patches/server/0089-Replace-shape-full-block-cache-with-hashtable.patch similarity index 95% rename from patches/server/0090-Replace-shape-full-block-cache-with-hashtable.patch rename to patches/server/0089-Replace-shape-full-block-cache-with-hashtable.patch index d31927d..41139c0 100644 --- a/patches/server/0090-Replace-shape-full-block-cache-with-hashtable.patch +++ b/patches/server/0089-Replace-shape-full-block-cache-with-hashtable.patch @@ -82,7 +82,7 @@ index 0000000000000000000000000000000000000000..48c32b224095f606669a38f0e200505a + +} diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java -index 4f91e4832a94c3facbc711fcae4cb5ad540a5ca0..7d6fab994d1308cb83057cecfe3d837f5063a5b1 100644 +index 9522e646529f3d849471931b4b3c0d133e7fcfc5..c4cff3592a39e519fc62664a21d368d8cac55271 100644 --- a/src/main/java/net/minecraft/world/level/block/Block.java +++ b/src/main/java/net/minecraft/world/level/block/Block.java @@ -11,6 +11,8 @@ import java.util.List; @@ -94,7 +94,7 @@ index 4f91e4832a94c3facbc711fcae4cb5ad540a5ca0..7d6fab994d1308cb83057cecfe3d837f import net.minecraft.SharedConstants; import net.minecraft.Util; import net.minecraft.core.BlockPos; -@@ -68,11 +70,12 @@ public class Block extends BlockBehaviour implements ItemLike { +@@ -67,11 +69,12 @@ public class Block extends BlockBehaviour implements ItemLike { private static final Logger LOGGER = LogUtils.getLogger(); private final Holder.Reference builtInRegistryHolder; public static final IdMapper BLOCK_STATE_REGISTRY = new IdMapper<>(); @@ -112,7 +112,7 @@ index 4f91e4832a94c3facbc711fcae4cb5ad540a5ca0..7d6fab994d1308cb83057cecfe3d837f public static final int UPDATE_NEIGHBORS = 1; public static final int UPDATE_CLIENTS = 2; public static final int UPDATE_INVISIBLE = 4; -@@ -279,7 +282,7 @@ public class Block extends BlockBehaviour implements ItemLike { +@@ -277,7 +280,7 @@ public class Block extends BlockBehaviour implements ItemLike { } public static boolean isShapeFullBlock(VoxelShape shape) { diff --git a/patches/server/0091-Avoid-Class-isAssignableFrom-call-in-ClassInstanceMu.patch b/patches/server/0090-Avoid-Class-isAssignableFrom-call-in-ClassInstanceMu.patch similarity index 100% rename from patches/server/0091-Avoid-Class-isAssignableFrom-call-in-ClassInstanceMu.patch rename to patches/server/0090-Avoid-Class-isAssignableFrom-call-in-ClassInstanceMu.patch diff --git a/patches/server/0092-Cache-BlockStatePairKey-hash.patch b/patches/server/0091-Cache-BlockStatePairKey-hash.patch similarity index 90% rename from patches/server/0092-Cache-BlockStatePairKey-hash.patch rename to patches/server/0091-Cache-BlockStatePairKey-hash.patch index f874a8c..0cba999 100644 --- a/patches/server/0092-Cache-BlockStatePairKey-hash.patch +++ b/patches/server/0091-Cache-BlockStatePairKey-hash.patch @@ -13,10 +13,10 @@ As part of: Lithium (https://github.com/CaffeineMC/lithium-fabric) Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java -index 7d6fab994d1308cb83057cecfe3d837f5063a5b1..52743a5865997506b5a53fdfd99cdbab67ae3d3f 100644 +index c4cff3592a39e519fc62664a21d368d8cac55271..e9d37ab8e2b072454c46d4520491beac5873a7ec 100644 --- a/src/main/java/net/minecraft/world/level/block/Block.java +++ b/src/main/java/net/minecraft/world/level/block/Block.java -@@ -605,11 +605,18 @@ public class Block extends BlockBehaviour implements ItemLike { +@@ -593,11 +593,18 @@ public class Block extends BlockBehaviour implements ItemLike { private final BlockState first; private final BlockState second; private final Direction direction; @@ -35,7 +35,7 @@ index 7d6fab994d1308cb83057cecfe3d837f5063a5b1..52743a5865997506b5a53fdfd99cdbab } public boolean equals(Object object) { -@@ -625,11 +632,7 @@ public class Block extends BlockBehaviour implements ItemLike { +@@ -613,11 +620,7 @@ public class Block extends BlockBehaviour implements ItemLike { } public int hashCode() { diff --git a/patches/server/0093-Cache-CubeVoxelShape-shape-array.patch b/patches/server/0092-Cache-CubeVoxelShape-shape-array.patch similarity index 100% rename from patches/server/0093-Cache-CubeVoxelShape-shape-array.patch rename to patches/server/0092-Cache-CubeVoxelShape-shape-array.patch diff --git a/patches/server/0094-Replace-division-by-multiplication-in-CubePointRange.patch b/patches/server/0093-Replace-division-by-multiplication-in-CubePointRange.patch similarity index 100% rename from patches/server/0094-Replace-division-by-multiplication-in-CubePointRange.patch rename to patches/server/0093-Replace-division-by-multiplication-in-CubePointRange.patch diff --git a/patches/server/0095-Replace-parts-by-size-in-CubePointRange.patch b/patches/server/0094-Replace-parts-by-size-in-CubePointRange.patch similarity index 100% rename from patches/server/0095-Replace-parts-by-size-in-CubePointRange.patch rename to patches/server/0094-Replace-parts-by-size-in-CubePointRange.patch diff --git a/patches/server/0096-Check-frozen-ticks-before-landing-block.patch b/patches/server/0095-Check-frozen-ticks-before-landing-block.patch similarity index 88% rename from patches/server/0096-Check-frozen-ticks-before-landing-block.patch rename to patches/server/0095-Check-frozen-ticks-before-landing-block.patch index 2d9219a..a100c72 100644 --- a/patches/server/0096-Check-frozen-ticks-before-landing-block.patch +++ b/patches/server/0095-Check-frozen-ticks-before-landing-block.patch @@ -13,10 +13,10 @@ As part of: Lithium (https://github.com/CaffeineMC/lithium-fabric) Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 8e739f81455ef41502b195c360d2e4243c29cda4..765835fd7e05e20c6642171a2e431702eebf2835 100644 +index 1016a70c2b468463cb74aa6bbfe3aa65eb297786..da2b72bdcd2ec7e1992a828c4d5a543b113a2592 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -566,11 +566,10 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -584,11 +584,10 @@ public abstract class LivingEntity extends Entity implements Attackable { } protected void tryAddFrost() { @@ -29,7 +29,7 @@ index 8e739f81455ef41502b195c360d2e4243c29cda4..765835fd7e05e20c6642171a2e431702 if (attributemodifiable == null) { return; -@@ -580,7 +579,6 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -598,7 +597,6 @@ public abstract class LivingEntity extends Entity implements Attackable { attributemodifiable.addTransientModifier(new AttributeModifier(LivingEntity.SPEED_MODIFIER_POWDER_SNOW_UUID, "Powder snow slow", (double) f, AttributeModifier.Operation.ADDITION)); } diff --git a/patches/server/0097-Faster-chunk-serialization.patch b/patches/server/0096-Faster-chunk-serialization.patch similarity index 97% rename from patches/server/0097-Faster-chunk-serialization.patch rename to patches/server/0096-Faster-chunk-serialization.patch index e58e05b..ca4c0be 100644 --- a/patches/server/0097-Faster-chunk-serialization.patch +++ b/patches/server/0096-Faster-chunk-serialization.patch @@ -213,7 +213,7 @@ index 0000000000000000000000000000000000000000..28ece835ca32532c8a3547e7b2122d20 + } +} diff --git a/src/main/java/net/minecraft/util/BitStorage.java b/src/main/java/net/minecraft/util/BitStorage.java -index 106610ccc74b70b557b01c61262d56c4f1147acf..ccf2a8544cf4cd1865d29b4e8cb8c058c74c7fe9 100644 +index 8bafd5fd7499ba4a04bf706cfd1e156073716e21..b7df9eac4ee23fa489368af82ec9c07dea6ebd75 100644 --- a/src/main/java/net/minecraft/util/BitStorage.java +++ b/src/main/java/net/minecraft/util/BitStorage.java @@ -1,6 +1,7 @@ @@ -232,7 +232,7 @@ index 106610ccc74b70b557b01c61262d56c4f1147acf..ccf2a8544cf4cd1865d29b4e8cb8c058 + void compact(Palette srcPalette, Palette dstPalette, short[] out); // Gale - Lithium - faster chunk serialization } diff --git a/src/main/java/net/minecraft/util/SimpleBitStorage.java b/src/main/java/net/minecraft/util/SimpleBitStorage.java -index 36e33923bf48e56c743ed043bcbc66bc32f0422f..9c8b23640fa45764601242c9345071267795a17f 100644 +index e4d0d7e8fc58b8f9f614d74a141e452166e0364c..7a1e8033e561b14ca8f04d8888634e91eee32b06 100644 --- a/src/main/java/net/minecraft/util/SimpleBitStorage.java +++ b/src/main/java/net/minecraft/util/SimpleBitStorage.java @@ -2,6 +2,7 @@ package net.minecraft.util; @@ -290,7 +290,7 @@ index 36e33923bf48e56c743ed043bcbc66bc32f0422f..9c8b23640fa45764601242c934507126 + } diff --git a/src/main/java/net/minecraft/util/ZeroBitStorage.java b/src/main/java/net/minecraft/util/ZeroBitStorage.java -index 75ca7ae6028e971f73988f5e71598696c0765cc8..41e795b67a138dea11f6d0270e7c36072810ef21 100644 +index 79b223ef3c86f3bc035e9abd84aa080b5d722f61..81ed765bfe50e3e469abbcdb8633bdacd2adf8ba 100644 --- a/src/main/java/net/minecraft/util/ZeroBitStorage.java +++ b/src/main/java/net/minecraft/util/ZeroBitStorage.java @@ -2,6 +2,7 @@ package net.minecraft.util; @@ -320,7 +320,7 @@ index acae3eb30e0689048937f479dc3070f0688abdad..4b79f0474a9013dd4fdb68c6363ca194 int onResize(int newBits, T object); } diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java -index 7c770d131d39da6900fdd22df36707d5f43e8cd0..a5e8c6b99952e2d17831a9622c6f8e457e85cd35 100644 +index 7f5547dc31aa53b2863f4c09f598fa88e7fe2afd..3cc5ba11eaae07ac7725fd5bbbaa05fef7e7bf3c 100644 --- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java @@ -22,8 +22,25 @@ import net.minecraft.util.Mth; @@ -349,7 +349,7 @@ index 7c770d131d39da6900fdd22df36707d5f43e8cd0..a5e8c6b99952e2d17831a9622c6f8e45 private static final int MIN_PALETTE_BITS = 0; private final PaletteResize dummyPaletteResize = (newSize, added) -> { return 0; -@@ -303,30 +320,54 @@ public class PalettedContainer implements PaletteResize, PalettedContainer +@@ -301,30 +318,54 @@ public class PalettedContainer implements PaletteResize, PalettedContainer public synchronized PalettedContainerRO.PackedData pack(IdMap idList, PalettedContainer.Strategy paletteProvider) { // Paper - synchronize this.acquire(); @@ -421,7 +421,7 @@ index 7c770d131d39da6900fdd22df36707d5f43e8cd0..a5e8c6b99952e2d17831a9622c6f8e45 } private static void swapPalette(int[] is, IntUnaryOperator applier) { -@@ -366,17 +407,37 @@ public class PalettedContainer implements PaletteResize, PalettedContainer +@@ -364,17 +405,37 @@ public class PalettedContainer implements PaletteResize, PalettedContainer @Override public void count(PalettedContainer.CountConsumer counter) { diff --git a/patches/server/0098-Update-boss-bar-within-tick.patch b/patches/server/0097-Update-boss-bar-within-tick.patch similarity index 95% rename from patches/server/0098-Update-boss-bar-within-tick.patch rename to patches/server/0097-Update-boss-bar-within-tick.patch index 2fc4bf4..501d5ea 100644 --- a/patches/server/0098-Update-boss-bar-within-tick.patch +++ b/patches/server/0097-Update-boss-bar-within-tick.patch @@ -13,7 +13,7 @@ As part of: Lithium (https://github.com/CaffeineMC/lithium-fabric) Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) diff --git a/src/main/java/net/minecraft/world/entity/raid/Raid.java b/src/main/java/net/minecraft/world/entity/raid/Raid.java -index 879c3bb661e24b9682b654def57c2800f4f8ca92..01f10b57f0e8f82fa60641275a6a920bf67f74aa 100644 +index f7399737548483905f3b5c08a03876b0da54b714..596ec820ab77bb6486a9bea446989770b775d5a6 100644 --- a/src/main/java/net/minecraft/world/entity/raid/Raid.java +++ b/src/main/java/net/minecraft/world/entity/raid/Raid.java @@ -107,6 +107,7 @@ public class Raid { diff --git a/patches/server/0099-Cache-ominous-banner-item.patch b/patches/server/0098-Cache-ominous-banner-item.patch similarity index 94% rename from patches/server/0099-Cache-ominous-banner-item.patch rename to patches/server/0098-Cache-ominous-banner-item.patch index 786f1d0..dc7a54f 100644 --- a/patches/server/0099-Cache-ominous-banner-item.patch +++ b/patches/server/0098-Cache-ominous-banner-item.patch @@ -26,7 +26,7 @@ index b8ef0f9c815799d54edcdb26dc0b4c1c281fc03e..9bec8e9e91676238b41d03d8044bfbb3 private final ItemPredicate chest; private final ItemPredicate legs; diff --git a/src/main/java/net/minecraft/world/entity/raid/Raid.java b/src/main/java/net/minecraft/world/entity/raid/Raid.java -index 01f10b57f0e8f82fa60641275a6a920bf67f74aa..246584452eb01b168d2372e1ccc4b357b63891df 100644 +index 596ec820ab77bb6486a9bea446989770b775d5a6..0db3cbcdbc7f73dd2c10bfb696209753fa33aa8d 100644 --- a/src/main/java/net/minecraft/world/entity/raid/Raid.java +++ b/src/main/java/net/minecraft/world/entity/raid/Raid.java @@ -702,7 +702,7 @@ public class Raid { @@ -54,7 +54,7 @@ index 01f10b57f0e8f82fa60641275a6a920bf67f74aa..246584452eb01b168d2372e1ccc4b357 public Raider getLeader(int wave) { return (Raider) this.groupToLeaderMap.get(wave); diff --git a/src/main/java/net/minecraft/world/entity/raid/Raider.java b/src/main/java/net/minecraft/world/entity/raid/Raider.java -index 99e9d46d42ddd0b451c6aeb847f1b295ebe5c697..088b25457f058404bdb535c5369a903768da6457 100644 +index 57fdcdaf54fd1c92a6e51a3a81789029096e5abe..e9e984959e08c139c8f2b4b9611bdba345ca9a13 100644 --- a/src/main/java/net/minecraft/world/entity/raid/Raider.java +++ b/src/main/java/net/minecraft/world/entity/raid/Raider.java @@ -47,7 +47,7 @@ public abstract class Raider extends PatrollingMonster { @@ -85,7 +85,7 @@ index 99e9d46d42ddd0b451c6aeb847f1b295ebe5c697..088b25457f058404bdb535c5369a9037 if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(this, item, 0, false).isCancelled()) { return; @@ -322,7 +322,7 @@ public abstract class Raider extends PatrollingMonster { - if (!this.mob.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items + if (!this.mob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items Raid raid = this.mob.getCurrentRaid(); - if (this.mob.hasActiveRaid() && !this.mob.getCurrentRaid().isOver() && this.mob.canBeLeader() && !ItemStack.matches(this.mob.getItemBySlot(EquipmentSlot.HEAD), Raid.getLeaderBannerInstance())) { diff --git a/patches/server/0100-Optimize-world-generation-chunk-and-block-access.patch b/patches/server/0099-Optimize-world-generation-chunk-and-block-access.patch similarity index 100% rename from patches/server/0100-Optimize-world-generation-chunk-and-block-access.patch rename to patches/server/0099-Optimize-world-generation-chunk-and-block-access.patch diff --git a/patches/server/0101-Cache-world-generator-sea-level.patch b/patches/server/0100-Cache-world-generator-sea-level.patch similarity index 93% rename from patches/server/0101-Cache-world-generator-sea-level.patch rename to patches/server/0100-Cache-world-generator-sea-level.patch index 98b1dc5..a896d0d 100644 --- a/patches/server/0101-Cache-world-generator-sea-level.patch +++ b/patches/server/0100-Cache-world-generator-sea-level.patch @@ -13,7 +13,7 @@ As part of: Lithium (https://github.com/CaffeineMC/lithium-fabric) Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) diff --git a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -index 0d8fd4eaf912eb4d40bb9f600dd2a8d5c21ab572..66205f6d99c629ce0e7e7d7b29394674bde115fb 100644 +index 58609a0911c4e32b6f80f050cd3d23f70ad75b1b..fc6f18f7606f6ede8264e0cf2d1ca60b03a7a364 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java +++ b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java @@ -62,12 +62,17 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { @@ -35,7 +35,7 @@ index 0d8fd4eaf912eb4d40bb9f600dd2a8d5c21ab572..66205f6d99c629ce0e7e7d7b29394674 }); } -@@ -397,7 +402,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { +@@ -394,7 +399,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { @Override public int getSeaLevel() { diff --git a/patches/server/0102-Skip-secondary-POI-sensor-if-absent.patch b/patches/server/0101-Skip-secondary-POI-sensor-if-absent.patch similarity index 100% rename from patches/server/0102-Skip-secondary-POI-sensor-if-absent.patch rename to patches/server/0101-Skip-secondary-POI-sensor-if-absent.patch diff --git a/patches/server/0103-Optimize-villager-data-storage.patch b/patches/server/0102-Optimize-villager-data-storage.patch similarity index 97% rename from patches/server/0103-Optimize-villager-data-storage.patch rename to patches/server/0102-Optimize-villager-data-storage.patch index fe0221b..19e4641 100644 --- a/patches/server/0103-Optimize-villager-data-storage.patch +++ b/patches/server/0102-Optimize-villager-data-storage.patch @@ -212,10 +212,10 @@ index 75dc06a3041bfdfb08c914eb50cfa282ae9eb2fe..53b0519bbc5d52490040eaf0fe449648 } else { brain.eraseMemory(MemoryModuleType.SECONDARY_JOB_SITE); diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index cf865df0763ebbe9ebbe74f32b0863e2636de63f..583f8bbb242305bf1bf825e254209108fd323ad4 100644 +index 10749bebb1c94ec1a74bacdce3b9b7903d8c4322..ae3628efe7628427c53bb7d0f7fc6e457a511b94 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -88,6 +88,7 @@ import net.minecraft.world.item.trading.MerchantOffers; +@@ -89,6 +89,7 @@ import net.minecraft.world.item.trading.MerchantOffers; import net.minecraft.world.level.Level; import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.phys.AABB; @@ -223,18 +223,18 @@ index cf865df0763ebbe9ebbe74f32b0863e2636de63f..583f8bbb242305bf1bf825e254209108 import org.slf4j.Logger; // CraftBukkit start -@@ -103,8 +104,9 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -104,8 +105,9 @@ public class Villager extends AbstractVillager implements ReputationEventHandler private static final EntityDataAccessor DATA_VILLAGER_DATA = SynchedEntityData.defineId(Villager.class, EntityDataSerializers.VILLAGER_DATA); public static final int BREEDING_FOOD_THRESHOLD = 12; public static final Map FOOD_POINTS = ImmutableMap.of(Items.BREAD, 4, Items.POTATO, 1, Items.CARROT, 1, Items.BEETROOT, 1); + public static final Item[] FOOD_POINTS_KEY_ARRAY = FOOD_POINTS.keySet().toArray(Item[]::new); // Gale - optimize villager data storage private static final int TRADES_PER_LEVEL = 2; -- private static final Set WANTED_ITEMS = ImmutableSet.of(Items.BREAD, Items.POTATO, Items.CARROT, Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT, new Item[]{Items.BEETROOT_SEEDS}); -+ private static final Item[] WANTED_ITEMS = {Items.BREAD, Items.POTATO, Items.CARROT, Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT, Items.BEETROOT_SEEDS}; // Gale - optimize villager data storage +- private static final Set WANTED_ITEMS = ImmutableSet.of(Items.BREAD, Items.POTATO, Items.CARROT, Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT, new Item[]{Items.BEETROOT_SEEDS, Items.TORCHFLOWER_SEEDS, Items.PITCHER_POD}); ++ private static final Item[] WANTED_ITEMS = {Items.BREAD, Items.POTATO, Items.CARROT, Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT, Items.BEETROOT_SEEDS, Items.TORCHFLOWER_SEEDS, Items.PITCHER_POD}; // Gale - optimize villager data storage private static final int MAX_GOSSIP_TOPICS = 10; private static final int GOSSIP_COOLDOWN = 1200; private static final int GOSSIP_DECAY_INTERVAL = 24000; -@@ -908,7 +910,28 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -914,7 +916,28 @@ public class Villager extends AbstractVillager implements ReputationEventHandler public boolean wantsToPickUp(ItemStack stack) { Item item = stack.getItem(); diff --git a/patches/server/0104-Skip-entity-move-if-movement-is-zero.patch b/patches/server/0103-Skip-entity-move-if-movement-is-zero.patch similarity index 88% rename from patches/server/0104-Skip-entity-move-if-movement-is-zero.patch rename to patches/server/0103-Skip-entity-move-if-movement-is-zero.patch index 98c7698..d3a6ed8 100644 --- a/patches/server/0104-Skip-entity-move-if-movement-is-zero.patch +++ b/patches/server/0103-Skip-entity-move-if-movement-is-zero.patch @@ -13,10 +13,10 @@ As part of: VMP (https://github.com/RelativityMC/VMP-fabric) Licensed under: MIT (https://opensource.org/licenses/MIT) diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 6990b9cf582f32a9ec259f5641eb1d58ffff2a9d..a45081dc23e2602a1ee3fbc05dbae82085604449 100644 +index 220880d8a723724174fe948765e108ecaf24872b..88e414d74a6b144b7c3db5bcd3a3d9841b9c051d 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -300,6 +300,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -302,6 +302,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { public float yRotO; public float xRotO; private AABB bb; @@ -24,7 +24,7 @@ index 6990b9cf582f32a9ec259f5641eb1d58ffff2a9d..a45081dc23e2602a1ee3fbc05dbae820 public boolean onGround; public boolean horizontalCollision; public boolean verticalCollision; -@@ -1025,6 +1026,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -1076,6 +1077,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { // Paper end - detailed watchdog information public void move(MoverType movementType, Vec3 movement) { @@ -36,7 +36,7 @@ index 6990b9cf582f32a9ec259f5641eb1d58ffff2a9d..a45081dc23e2602a1ee3fbc05dbae820 // Paper start - detailed watchdog information io.papermc.paper.util.TickThread.ensureTickThread("Cannot move an entity off-main"); synchronized (this.posLock) { -@@ -3851,6 +3857,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -3980,6 +3986,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { } public final void setBoundingBox(AABB boundingBox) { diff --git a/patches/server/0105-Store-mob-counts-in-an-array.patch b/patches/server/0104-Store-mob-counts-in-an-array.patch similarity index 100% rename from patches/server/0105-Store-mob-counts-in-an-array.patch rename to patches/server/0104-Store-mob-counts-in-an-array.patch diff --git a/patches/server/0106-Use-linked-map-for-entity-trackers.patch b/patches/server/0105-Use-linked-map-for-entity-trackers.patch similarity index 91% rename from patches/server/0106-Use-linked-map-for-entity-trackers.patch rename to patches/server/0105-Use-linked-map-for-entity-trackers.patch index 8d95a29..98a7ea8 100644 --- a/patches/server/0106-Use-linked-map-for-entity-trackers.patch +++ b/patches/server/0105-Use-linked-map-for-entity-trackers.patch @@ -13,7 +13,7 @@ As part of: VMP (https://github.com/RelativityMC/VMP-fabric) Licensed under: MIT (https://opensource.org/licenses/MIT) diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 014ca25e17915d2a3a8e2a8d37265a4d7bc09d24..0a39aaf5bc54e1fb7fb4bada2a15fea9b73af731 100644 +index caa765c6264e14cc66973267f94074c7056fe122..780deea4805dfdd80d47e20ca1c2fcac17e5cabc 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -14,6 +14,7 @@ import com.mojang.datafixers.util.Either; @@ -24,7 +24,7 @@ index 014ca25e17915d2a3a8e2a8d37265a4d7bc09d24..0a39aaf5bc54e1fb7fb4bada2a15fea9 import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ByteMap; -@@ -297,7 +298,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -294,7 +295,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // Paper - rewrite chunk system this.tickingGenerated = new AtomicInteger(); this.playerMap = new PlayerMap(); diff --git a/patches/server/0107-Optimize-noise-generation.patch b/patches/server/0106-Optimize-noise-generation.patch similarity index 100% rename from patches/server/0107-Optimize-noise-generation.patch rename to patches/server/0106-Optimize-noise-generation.patch diff --git a/patches/server/0108-Optimize-sheep-offspring-color.patch b/patches/server/0107-Optimize-sheep-offspring-color.patch similarity index 94% rename from patches/server/0108-Optimize-sheep-offspring-color.patch rename to patches/server/0107-Optimize-sheep-offspring-color.patch index 2dc0453..d795808 100644 --- a/patches/server/0108-Optimize-sheep-offspring-color.patch +++ b/patches/server/0107-Optimize-sheep-offspring-color.patch @@ -46,10 +46,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -index fb3777e158065a6ce306a2a6e66bec053da2aeb4..8b17dd156aa321686570da1e620107274adca56f 100644 +index c0e89262c596fbdd0bb3c3f76baccb17a1bb5fcd..d8f99e1221609d481ee79ee31f645731f34e1021 100644 --- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java +++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -@@ -391,7 +391,59 @@ public class Sheep extends Animal implements Shearable { +@@ -392,7 +392,59 @@ public class Sheep extends Animal implements Shearable { return super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt); } @@ -98,11 +98,11 @@ index fb3777e158065a6ce306a2a6e66bec053da2aeb4..8b17dd156aa321686570da1e62010727 + private DyeColor getOffspringColor(Animal firstParent, Animal secondParent) { + // Gale start - carpet-fixes - optimize sheep offspring color -+ if (firstParent.level != null && firstParent.level.galeConfig().smallOptimizations.useOptimizedSheepOffspringColor) { ++ if (firstParent.level() != null && firstParent.level().galeConfig().smallOptimizations.useOptimizedSheepOffspringColor) { + DyeColor firstColor = ((Sheep)firstParent).getColor(); + DyeColor secondColor = ((Sheep)secondParent).getColor(); + DyeColor col = properDye(firstColor,secondColor); -+ if (col == null) col = this.level.random.nextBoolean() ? firstColor : secondColor; ++ if (col == null) col = this.level().random.nextBoolean() ? firstColor : secondColor; + return col; + } + // Gale end - carpet-fixes - optimize sheep offspring color diff --git a/patches/server/0110-Hide-flames-on-entities-with-fire-resistance.patch b/patches/server/0108-Hide-flames-on-entities-with-fire-resistance.patch similarity index 85% rename from patches/server/0110-Hide-flames-on-entities-with-fire-resistance.patch rename to patches/server/0108-Hide-flames-on-entities-with-fire-resistance.patch index e3dc83b..a881efb 100644 --- a/patches/server/0110-Hide-flames-on-entities-with-fire-resistance.patch +++ b/patches/server/0108-Hide-flames-on-entities-with-fire-resistance.patch @@ -13,13 +13,13 @@ As part of: Slice (https://github.com/Cryptite/Slice) Licensed under: MIT (https://opensource.org/licenses/MIT) diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index a45081dc23e2602a1ee3fbc05dbae82085604449..275184f437b7bdb6035fc468dc4396b915af1d50 100644 +index 88e414d74a6b144b7c3db5bcd3a3d9841b9c051d..b30e7c2fe1d9247a3aa38e9d2b50f768181c26f8 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -872,7 +872,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -878,7 +878,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - this.checkOutOfWorld(); - if (!this.level.isClientSide) { + this.checkBelowWorld(); + if (!this.level().isClientSide) { + // Gale start - Slice - hide flames on entities with fire resistance + if (this instanceof net.minecraft.world.entity.LivingEntity livingEntity) { + this.setSharedFlagOnFire(this.remainingFireTicks > 0 && (!this.level.galeConfig().gameplayMechanics.hideFlamesOnEntitiesWithFireResistance || !livingEntity.hasEffect(net.minecraft.world.effect.MobEffects.FIRE_RESISTANCE))); @@ -31,10 +31,10 @@ index a45081dc23e2602a1ee3fbc05dbae82085604449..275184f437b7bdb6035fc468dc4396b9 this.firstTick = false; diff --git a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -index cdc53823b4903ab6a443a626503f62b732cc97cb..be1dc2ec007456f864941bf3e3d246610808888c 100644 +index 3ebd9c177453bf720c40c7f9d41e28a234c80467..4329d9bbfe002a4e505b0a2f13063aa8ca736f8f 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java +++ b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -@@ -294,6 +294,7 @@ public class GaleWorldConfiguration extends ConfigurationPart { +@@ -292,6 +292,7 @@ public class GaleWorldConfiguration extends ConfigurationPart { public boolean entitiesCanEatBlocksInNonTickingChunks = false; // Gale - Purpur - prevent entities eating blocks in non-ticking chunks public boolean arrowMovementResetsDespawnCounter = true; // Gale - Purpur - make arrow movement resetting despawn counter configurable diff --git a/patches/server/0109-Ignore-durability-change-equipment-updates.patch b/patches/server/0109-Ignore-durability-change-equipment-updates.patch deleted file mode 100644 index d8b5abe..0000000 --- a/patches/server/0109-Ignore-durability-change-equipment-updates.patch +++ /dev/null @@ -1,67 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Wed, 30 Nov 2022 15:01:42 +0100 -Subject: [PATCH] Ignore durability change equipment updates - -License: MIT (https://opensource.org/licenses/MIT) -Gale - https://galemc.org - -This patch is based on the following patch: -"Ignore durability change equipment updates" -By: Cryptite -As part of: Slice (https://github.com/Cryptite/Slice) -Licensed under: MIT (https://opensource.org/licenses/MIT) - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 765835fd7e05e20c6642171a2e431702eebf2835..55489052b3472c43c71de3b5ec907f2526d81caf 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -3113,7 +3113,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - } - - public boolean equipmentHasChanged(ItemStack stack, ItemStack stack2) { -- return !ItemStack.matches(stack2, stack); -+ return this.level.galeConfig().smallOptimizations.processFullEquipmentChangeIfOnlyDurabilityChanged ? !ItemStack.matches(stack2, stack) : !ItemStack.isSameIgnoreDurability(stack2, stack); // Gale - Slice - ignore durability change equipment updates - } - - private void handleHandSwap(Map equipmentChanges) { -diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index 29a11bbcab84e66cb8adf9df2ea9feef4da601e8..f2c3d0913ee37e4f2d5f409f47581be9d8b2fa3d 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -748,10 +748,22 @@ public final class ItemStack { - return left == right ? true : (!left.isEmpty() && !right.isEmpty() ? left.sameItem(right) : false); - } - -+ // Gale start - Slice - ignore durability change equipment updates -+ public static boolean isSameIgnoreDurability(ItemStack left, ItemStack right) { -+ return left == right ? true : (!left.isEmpty() && !right.isEmpty() ? left.sameItemStackIgnoreDurability(right) : false); -+ } -+ // Gale end - Slice - ignore durability change equipment updates -+ - public boolean sameItem(ItemStack stack) { - return !stack.isEmpty() && this.is(stack.getItem()); - } - -+ // Gale start - Slice - ignore durability change equipment updates -+ public boolean sameItemStackIgnoreDurability(ItemStack stack) { -+ return !this.isDamageableItem() ? this.sameItem(stack) : !stack.isEmpty() && this.is(stack.getItem()); -+ } -+ // Gale end - Slice - ignore durability change equipment updates -+ - public static boolean isSameItemSameTags(ItemStack stack, ItemStack otherStack) { - return stack.is(otherStack.getItem()) && ItemStack.tagMatches(stack, otherStack); - } -diff --git a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -index 3ebd9c177453bf720c40c7f9d41e28a234c80467..cdc53823b4903ab6a443a626503f62b732cc97cb 100644 ---- a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -+++ b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -@@ -62,6 +62,8 @@ public class GaleWorldConfiguration extends ConfigurationPart { - public boolean useOptimizedSheepOffspringColor = true; - // Gale end - carpet-fixes - optimize sheep offspring color - -+ public boolean processFullEquipmentChangeIfOnlyDurabilityChanged = false; // Gale - Slice - ignore durability change equipment updates -+ - // Gale start - Airplane - reduce projectile chunk loading - public MaxProjectileChunkLoads maxProjectileChunkLoads; - public class MaxProjectileChunkLoads extends ConfigurationPart { diff --git a/patches/server/0111-Skip-cloning-advancement-criteria.patch b/patches/server/0109-Skip-cloning-advancement-criteria.patch similarity index 84% rename from patches/server/0111-Skip-cloning-advancement-criteria.patch rename to patches/server/0109-Skip-cloning-advancement-criteria.patch index 753e347..198eccd 100644 --- a/patches/server/0111-Skip-cloning-advancement-criteria.patch +++ b/patches/server/0109-Skip-cloning-advancement-criteria.patch @@ -13,11 +13,11 @@ As part of: Mirai (https://github.com/etil2jz/Mirai) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/advancements/Advancement.java b/src/main/java/net/minecraft/advancements/Advancement.java -index 31ef13a708db2e4a664b30090a562eb6e4597bab..2220a078fd8c1016c92150aa6d33ca6d3e08a97f 100644 +index 81359be381fc9bcb56a9cc83e70a6afa6e838c6a..08940582874e9388115b5ec815f997dedad69a7b 100644 --- a/src/main/java/net/minecraft/advancements/Advancement.java +++ b/src/main/java/net/minecraft/advancements/Advancement.java -@@ -45,7 +45,7 @@ public class Advancement { - public Advancement(ResourceLocation id, @Nullable Advancement parent, @Nullable DisplayInfo display, AdvancementRewards rewards, Map criteria, String[][] requirements) { +@@ -46,7 +46,7 @@ public class Advancement { + public Advancement(ResourceLocation id, @Nullable Advancement parent, @Nullable DisplayInfo display, AdvancementRewards rewards, Map criteria, String[][] requirements, boolean sendsTelemetryEvent) { this.id = id; this.display = display; - this.criteria = ImmutableMap.copyOf(criteria); diff --git a/patches/server/0112-Reduce-block-destruction-packet-allocations.patch b/patches/server/0110-Reduce-block-destruction-packet-allocations.patch similarity index 89% rename from patches/server/0112-Reduce-block-destruction-packet-allocations.patch rename to patches/server/0110-Reduce-block-destruction-packet-allocations.patch index 1ee0d52..6c07e01 100644 --- a/patches/server/0112-Reduce-block-destruction-packet-allocations.patch +++ b/patches/server/0110-Reduce-block-destruction-packet-allocations.patch @@ -13,10 +13,10 @@ As part of: SportPaper (https://github.com/Electroid/SportPaper) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 5eb689cb445c0cf8b645e543c8c3a324b1eca536..781f0fefe3e1ed7db92d81890e6b39ca7aa91f7a 100644 +index 9879599c38db4f3332c347b6b0c26034bc7749bc..1462f9d4f2cdf4071fb002d602783866a5a3d285 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1553,7 +1553,17 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1695,7 +1695,17 @@ public class ServerLevel extends Level implements WorldGenLevel { @Override public void destroyBlockProgress(int entityId, BlockPos pos, int progress) { @@ -35,7 +35,7 @@ index 5eb689cb445c0cf8b645e543c8c3a324b1eca536..781f0fefe3e1ed7db92d81890e6b39ca // CraftBukkit start Player entityhuman = null; -@@ -1576,7 +1586,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1718,7 +1728,7 @@ public class ServerLevel extends Level implements WorldGenLevel { // CraftBukkit end if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0D) { diff --git a/patches/server/0113-Send-set-head-rotation-packets-only-for-living-entit.patch b/patches/server/0111-Send-set-head-rotation-packets-only-for-living-entit.patch similarity index 77% rename from patches/server/0113-Send-set-head-rotation-packets-only-for-living-entit.patch rename to patches/server/0111-Send-set-head-rotation-packets-only-for-living-entit.patch index ad748aa..18c6f86 100644 --- a/patches/server/0113-Send-set-head-rotation-packets-only-for-living-entit.patch +++ b/patches/server/0111-Send-set-head-rotation-packets-only-for-living-entit.patch @@ -13,26 +13,26 @@ As part of: SportPaper (https://github.com/Electroid/SportPaper) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index 834b497053f4fa20cf94c00e1ee2db4838bdf233..b79e302cf4e0eae858e2322b5b260282b1939963 100644 +index 0ced61876ad4790c0a0c8e24be721241fa7fd319..7ad872f8f769dfe6e3b6a0b8f83357076b33d22b 100644 --- a/src/main/java/net/minecraft/server/level/ServerEntity.java +++ b/src/main/java/net/minecraft/server/level/ServerEntity.java -@@ -361,7 +361,9 @@ public class ServerEntity { +@@ -355,7 +355,9 @@ public class ServerEntity { // CraftBukkit start - Fix for nonsensical head yaw this.yHeadRotp = Mth.floor(this.entity.getYHeadRot() * 256.0F / 360.0F); + if (!this.level.galeConfig().smallOptimizations.sendSetHeadRotationPacketsOnlyForLivingEntities || this.entity instanceof LivingEntity) { // Gale - SportPaper - send set head rotation packets only for living entities - consumer.accept(new ClientboundRotateHeadPacket(this.entity, (byte) this.yHeadRotp)); + sender.accept(new ClientboundRotateHeadPacket(this.entity, (byte) this.yHeadRotp)); + } // Gale - SportPaper - send set head rotation packets only for living entities // CraftBukkit end - if (this.entity instanceof LivingEntity) { + if (!this.entity.getPassengers().isEmpty()) { diff --git a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -index be1dc2ec007456f864941bf3e3d246610808888c..49ac6d7d324707b86364b393123ad695cfbe28e2 100644 +index 4329d9bbfe002a4e505b0a2f13063aa8ca736f8f..edb4e3b5bde5309bd61a6f1605c71633facae7af 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java +++ b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -@@ -64,6 +64,8 @@ public class GaleWorldConfiguration extends ConfigurationPart { - - public boolean processFullEquipmentChangeIfOnlyDurabilityChanged = false; // Gale - Slice - ignore durability change equipment updates +@@ -62,6 +62,8 @@ public class GaleWorldConfiguration extends ConfigurationPart { + public boolean useOptimizedSheepOffspringColor = true; + // Gale end - carpet-fixes - optimize sheep offspring color + public boolean sendSetHeadRotationPacketsOnlyForLivingEntities = true; // Gale - SportPaper - send set head rotation packets only for living entities + diff --git a/patches/server/0114-Spread-out-sending-all-player-info.patch b/patches/server/0112-Spread-out-sending-all-player-info.patch similarity index 92% rename from patches/server/0114-Spread-out-sending-all-player-info.patch rename to patches/server/0112-Spread-out-sending-all-player-info.patch index 5ccbf87..ba0c813 100644 --- a/patches/server/0114-Spread-out-sending-all-player-info.patch +++ b/patches/server/0112-Spread-out-sending-all-player-info.patch @@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index b03789c629c87d524e24a6e3bb893ae2bac1d5c1..b123164e6a838412feb3f011c3097932d607b064 100644 +index ddde03a81d7a39188ac26c16658301e138356453..25566ec9b692499b30b16bf5c9c5436936c5ca9b 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -15,6 +15,8 @@ import java.net.SocketAddress; @@ -62,15 +62,15 @@ index b03789c629c87d524e24a6e3bb893ae2bac1d5c1..b123164e6a838412feb3f011c3097932 private final Map playersByUUID = Maps.newHashMap(); private final UserBanList bans; private final IpBanList ipBans; -@@ -315,6 +318,7 @@ public abstract class PlayerList { +@@ -322,6 +325,7 @@ public abstract class PlayerList { - // player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); // Paper + // entityplayer.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); // CraftBukkit - replaced with loop below this.players.add(player); + this.addToSendAllPlayerInfoBuckets(player); // Gale - Purpur - spread out sending all player info this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Spigot this.playersByUUID.put(player.getUUID(), player); // this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(entityplayer))); // CraftBukkit - replaced with loop below -@@ -649,6 +653,7 @@ public abstract class PlayerList { +@@ -656,6 +660,7 @@ public abstract class PlayerList { worldserver.removePlayerImmediately(entityplayer, Entity.RemovalReason.UNLOADED_WITH_PLAYER); entityplayer.getAdvancements().stopListening(); this.players.remove(entityplayer); @@ -78,15 +78,15 @@ index b03789c629c87d524e24a6e3bb893ae2bac1d5c1..b123164e6a838412feb3f011c3097932 this.playersByName.remove(entityplayer.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot this.server.getCustomBossEvents().onPlayerDisconnect(entityplayer); UUID uuid = entityplayer.getUUID(); -@@ -802,6 +807,7 @@ public abstract class PlayerList { +@@ -809,6 +814,7 @@ public abstract class PlayerList { // Paper end entityplayer.stopRiding(); // CraftBukkit this.players.remove(entityplayer); + this.removeFromSendAllPlayerInfoBuckets(entityplayer); // Gale - Purpur - spread out sending all player info this.playersByName.remove(entityplayer.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot - entityplayer.getLevel().removePlayerImmediately(entityplayer, Entity.RemovalReason.DISCARDED); + entityplayer.serverLevel().removePlayerImmediately(entityplayer, Entity.RemovalReason.DISCARDED); BlockPos blockposition = entityplayer.getRespawnPosition(); -@@ -939,6 +945,7 @@ public abstract class PlayerList { +@@ -946,6 +952,7 @@ public abstract class PlayerList { if (!entityplayer.connection.isDisconnected()) { worldserver1.addRespawnedPlayer(entityplayer1); this.players.add(entityplayer1); @@ -94,7 +94,7 @@ index b03789c629c87d524e24a6e3bb893ae2bac1d5c1..b123164e6a838412feb3f011c3097932 this.playersByName.put(entityplayer1.getScoreboardName().toLowerCase(java.util.Locale.ROOT), entityplayer1); // Spigot this.playersByUUID.put(entityplayer1.getUUID(), entityplayer1); } -@@ -991,20 +998,55 @@ public abstract class PlayerList { +@@ -998,20 +1005,55 @@ public abstract class PlayerList { this.sendPlayerPermissionLevel(player, i); } @@ -157,7 +157,7 @@ index b03789c629c87d524e24a6e3bb893ae2bac1d5c1..b123164e6a838412feb3f011c3097932 } diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 65e356449e9c96c937b45eaab94aa6cc4296fcb9..b28e0b2d2dfc46fa568ab23dc003d1c9abb1f18a 100644 +index 6a906b1f37e52472527e9f19e965be9f155dd404..af8b5282df19c92c5f1394dc9d889012ce509f32 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java @@ -40,6 +40,7 @@ import net.minecraft.network.syncher.SynchedEntityData; @@ -168,7 +168,7 @@ index 65e356449e9c96c937b45eaab94aa6cc4296fcb9..b28e0b2d2dfc46fa568ab23dc003d1c9 import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; -@@ -197,12 +198,15 @@ public abstract class Player extends LivingEntity { +@@ -198,12 +199,15 @@ public abstract class Player extends LivingEntity { } // CraftBukkit end diff --git a/patches/server/0115-Optimize-player-list-for-sending-player-info.patch b/patches/server/0113-Optimize-player-list-for-sending-player-info.patch similarity index 95% rename from patches/server/0115-Optimize-player-list-for-sending-player-info.patch rename to patches/server/0113-Optimize-player-list-for-sending-player-info.patch index 127bdf5..944b53c 100644 --- a/patches/server/0115-Optimize-player-list-for-sending-player-info.patch +++ b/patches/server/0113-Optimize-player-list-for-sending-player-info.patch @@ -37,10 +37,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index b123164e6a838412feb3f011c3097932d607b064..f87d10098ea3e3c35e574dc388da6eba4bc6422c 100644 +index 25566ec9b692499b30b16bf5c9c5436936c5ca9b..1678492a2e65bea22e9c10d6680218160dda2c2d 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -1034,14 +1034,18 @@ public abstract class PlayerList { +@@ -1041,14 +1041,18 @@ public abstract class PlayerList { // Gale start - Purpur - spread out sending all player info ServerPlayer[] sendAllPlayerInfoBucket = this.sendAllPlayerInfoBuckets[this.sendAllPlayerInfoIn]; if (sendAllPlayerInfoBucket != null) { diff --git a/patches/server/0116-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch b/patches/server/0114-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch similarity index 93% rename from patches/server/0116-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch rename to patches/server/0114-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch index dc128cd..52b57b4 100644 --- a/patches/server/0116-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch +++ b/patches/server/0114-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch @@ -37,10 +37,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index 28d78c5d593302d8fe376686db6402cafa5ccb86..1a28f9b348a24448bd4a327e1bf0dfab4dc301f5 100644 +index 3396c696a380f8ac64740acee887de823920610f..4d7fada64d600d799ca6d9dbba314d87a5d44ffe 100644 --- a/src/main/java/net/minecraft/commands/Commands.java +++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -447,6 +447,7 @@ public class Commands { +@@ -449,6 +449,7 @@ public class Commands { private void runSync(ServerPlayer player, Collection bukkit, RootCommandNode rootcommandnode) { // Paper end - Async command map building new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper @@ -48,7 +48,7 @@ index 28d78c5d593302d8fe376686db6402cafa5ccb86..1a28f9b348a24448bd4a327e1bf0dfab PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit)); event.getPlayer().getServer().getPluginManager().callEvent(event); -@@ -457,6 +458,7 @@ public class Commands { +@@ -459,6 +460,7 @@ public class Commands { } } // CraftBukkit end diff --git a/patches/server/0117-Send-multiple-keep-alive-packets.patch b/patches/server/0115-Send-multiple-keep-alive-packets.patch similarity index 96% rename from patches/server/0117-Send-multiple-keep-alive-packets.patch rename to patches/server/0115-Send-multiple-keep-alive-packets.patch index 2dc7e0e..8d57e32 100644 --- a/patches/server/0117-Send-multiple-keep-alive-packets.patch +++ b/patches/server/0115-Send-multiple-keep-alive-packets.patch @@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 9f2f019ad39b0aa16ebe0eabd79fc5beba897959..851c97fc36e31926a2f241759e12daeee09f45c9 100644 +index 574c8a2680c66d9e8bc14b1128652f1f4d16210b..31dc612cd8f84fec4138b5d9ed4ac093c3e202ce 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -7,6 +7,8 @@ import com.mojang.brigadier.StringReader; @@ -91,7 +91,7 @@ index 9f2f019ad39b0aa16ebe0eabd79fc5beba897959..851c97fc36e31926a2f241759e12daee if (this.keepAlivePending) { if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getScoreboardName()); // more info -@@ -3503,6 +3524,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic +@@ -3465,6 +3486,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic @Override public void handleKeepAlive(ServerboundKeepAlivePacket packet) { @@ -105,7 +105,7 @@ index 9f2f019ad39b0aa16ebe0eabd79fc5beba897959..851c97fc36e31926a2f241759e12daee + } + } else + // Gale end - Purpur - send multiple keep-alive packets - //PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); // CraftBukkit // Paper - This shouldn't be on the main thread + //PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // CraftBukkit // Paper - This shouldn't be on the main thread if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) { int i = (int) (Util.getMillis() - this.keepAliveTime); diff --git a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java diff --git a/patches/server/0118-Make-slow-login-timeout-configurable.patch b/patches/server/0116-Make-slow-login-timeout-configurable.patch similarity index 100% rename from patches/server/0118-Make-slow-login-timeout-configurable.patch rename to patches/server/0116-Make-slow-login-timeout-configurable.patch diff --git a/patches/server/0119-Make-max-interaction-distance-configurable.patch b/patches/server/0117-Make-max-interaction-distance-configurable.patch similarity index 73% rename from patches/server/0119-Make-max-interaction-distance-configurable.patch rename to patches/server/0117-Make-max-interaction-distance-configurable.patch index 175d51a..f34b85c 100644 --- a/patches/server/0119-Make-max-interaction-distance-configurable.patch +++ b/patches/server/0117-Make-max-interaction-distance-configurable.patch @@ -7,20 +7,20 @@ License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -index 1d33c02088c150189d7f4b0aa27f6a1de96b11cf..4c8275728175ebc1fdc8d0e8d0ba8398cefa0e17 100644 +index 1635fee928d64f4d2c336dca6675ed4641918830..d65b9d2d9070d8dee2c144c9b37d6605a59cfafe 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -@@ -173,7 +173,7 @@ public class ServerPlayerGameMode { +@@ -174,7 +174,7 @@ public class ServerPlayerGameMode { private void debugLogging(BlockPos pos, boolean success, int sequence, String reason) {} public void handleBlockBreakAction(BlockPos pos, ServerboundPlayerActionPacket.Action action, Direction direction, int worldHeight, int sequence) { - if (this.player.getEyePosition().distanceToSqr(Vec3.atCenterOf(pos)) > ServerGamePacketListenerImpl.MAX_INTERACTION_DISTANCE) { -+ if (this.player.getEyePosition().distanceToSqr(Vec3.atCenterOf(pos)) > ServerGamePacketListenerImpl.getMaxInteractionDistanceSquared(this.player.level)) { // Gale - make max interaction distance configurable ++ if (this.player.getEyePosition().distanceToSqr(Vec3.atCenterOf(pos)) > ServerGamePacketListenerImpl.getMaxInteractionDistanceSquared(this.player.level())) { // Gale - make max interaction distance configurable if (true) return; // Paper - Don't notify if unreasonably far away this.debugLogging(pos, false, sequence, "too far"); } else if (pos.getY() >= worldHeight) { diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 851c97fc36e31926a2f241759e12daeee09f45c9..5d61d3ead44175654f39760f9a7fdd4914b002b3 100644 +index 31dc612cd8f84fec4138b5d9ed4ac093c3e202ce..79dba3796df83d0127db6e29f38a35e1d3289724 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -254,7 +254,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic @@ -46,29 +46,50 @@ index 851c97fc36e31926a2f241759e12daeee09f45c9..5d61d3ead44175654f39760f9a7fdd49 @Override public void tick() { if (this.ackBlockChangesUpTo > -1) { -@@ -1968,7 +1975,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic +@@ -1966,7 +1973,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic BlockPos blockposition = movingobjectpositionblock.getBlockPos(); Vec3 vec3d1 = Vec3.atCenterOf(blockposition); - if (this.player.getEyePosition().distanceToSqr(vec3d1) <= ServerGamePacketListenerImpl.MAX_INTERACTION_DISTANCE) { -+ if (this.player.getEyePosition().distanceToSqr(vec3d1) <= ServerGamePacketListenerImpl.getMaxInteractionDistanceSquared(this.player.level)) { // Gale - make max interaction distance configurable ++ if (this.player.getEyePosition().distanceToSqr(vec3d1) <= ServerGamePacketListenerImpl.getMaxInteractionDistanceSquared(this.player.level())) { // Gale - make max interaction distance configurable Vec3 vec3d2 = vec3d.subtract(vec3d1); double d0 = 1.0000001D; -@@ -2827,7 +2834,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic +@@ -2825,7 +2832,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic AABB axisalignedbb = entity.getBoundingBox(); - if (axisalignedbb.distanceToSqr(this.player.getEyePosition()) < ServerGamePacketListenerImpl.MAX_INTERACTION_DISTANCE) { -+ if (axisalignedbb.distanceToSqr(this.player.getEyePosition()) < ServerGamePacketListenerImpl.getMaxInteractionDistanceSquared(this.player.level)) { // Gale - make max interaction distance configurable ++ if (axisalignedbb.distanceToSqr(this.player.getEyePosition()) < ServerGamePacketListenerImpl.getMaxInteractionDistanceSquared(this.player.level())) { // Gale - make max interaction distance configurable packet.dispatch(new ServerboundInteractPacket.Handler() { private void performInteraction(InteractionHand enumhand, ServerGamePacketListenerImpl.EntityInteraction playerconnection_a, PlayerInteractEntityEvent event) { // CraftBukkit ItemStack itemstack = ServerGamePacketListenerImpl.this.player.getItemInHand(enumhand); +diff --git a/src/main/java/net/minecraft/world/item/BrushItem.java b/src/main/java/net/minecraft/world/item/BrushItem.java +index d025232b43af3cb8dc28dff2e3e05c72e490901d..06d75d94af6aff29d542d0c5e150d8f7f8632e82 100644 +--- a/src/main/java/net/minecraft/world/item/BrushItem.java ++++ b/src/main/java/net/minecraft/world/item/BrushItem.java +@@ -29,7 +29,6 @@ import net.minecraft.world.phys.Vec3; + public class BrushItem extends Item { + public static final int ANIMATION_DURATION = 10; + private static final int USE_DURATION = 200; +- private static final double MAX_BRUSH_DISTANCE = Math.sqrt(ServerGamePacketListenerImpl.MAX_INTERACTION_DISTANCE) - 1.0D; + + public BrushItem(Item.Properties settings) { + super(settings); +@@ -108,7 +107,7 @@ public class BrushItem extends Item { + private HitResult calculateHitResult(LivingEntity user) { + return ProjectileUtil.getHitResultOnViewVector(user, (entity) -> { + return !entity.isSpectator() && entity.isPickable(); +- }, MAX_BRUSH_DISTANCE); ++ }, Math.sqrt(ServerGamePacketListenerImpl.getMaxInteractionDistanceSquared(user.level())) - 1.0D); // Gale - make max interaction distance configurable + } + + public void spawnDustParticles(Level world, BlockHitResult hitResult, BlockState state, Vec3 userRotation, HumanoidArm arm) { diff --git a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -index 49ac6d7d324707b86364b393123ad695cfbe28e2..a16c549ee1715c24acb93d17da396355e16e9158 100644 +index edb4e3b5bde5309bd61a6f1605c71633facae7af..c0274ed66bfe3a2bd8c67d0af807bc090dfabf7d 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java +++ b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -@@ -275,7 +275,7 @@ public class GaleWorldConfiguration extends ConfigurationPart { +@@ -273,7 +273,7 @@ public class GaleWorldConfiguration extends ConfigurationPart { } public GameplayMechanics gameplayMechanics; @@ -77,7 +98,7 @@ index 49ac6d7d324707b86364b393123ad695cfbe28e2..a16c549ee1715c24acb93d17da396355 // Gale start - variable entity wake-up duration /** -@@ -298,6 +298,20 @@ public class GaleWorldConfiguration extends ConfigurationPart { +@@ -296,6 +296,20 @@ public class GaleWorldConfiguration extends ConfigurationPart { public boolean arrowMovementResetsDespawnCounter = true; // Gale - Purpur - make arrow movement resetting despawn counter configurable public boolean hideFlamesOnEntitiesWithFireResistance = false; // Gale - Slice - hide flames on entities with fire resistance @@ -98,7 +119,7 @@ index 49ac6d7d324707b86364b393123ad695cfbe28e2..a16c549ee1715c24acb93d17da396355 public Fixes fixes; public class Fixes extends ConfigurationPart { -@@ -332,6 +346,11 @@ public class GaleWorldConfiguration extends ConfigurationPart { +@@ -330,6 +344,11 @@ public class GaleWorldConfiguration extends ConfigurationPart { } diff --git a/patches/server/0120-Load-portal-destination-chunk-before-entity-teleport.patch b/patches/server/0118-Load-portal-destination-chunk-before-entity-teleport.patch similarity index 90% rename from patches/server/0120-Load-portal-destination-chunk-before-entity-teleport.patch rename to patches/server/0118-Load-portal-destination-chunk-before-entity-teleport.patch index a3e8916..5095f5b 100644 --- a/patches/server/0120-Load-portal-destination-chunk-before-entity-teleport.patch +++ b/patches/server/0118-Load-portal-destination-chunk-before-entity-teleport.patch @@ -13,10 +13,10 @@ As part of: MultiPaper (https://github.com/MultiPaper/MultiPaper) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 275184f437b7bdb6035fc468dc4396b915af1d50..593d5bed11d393f2094936d52d81b3e87a35fcf0 100644 +index b30e7c2fe1d9247a3aa38e9d2b50f768181c26f8..a3243bba3a1afe23a2c4bf3ecb01f5fcf07837ce 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3477,6 +3477,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -3601,6 +3601,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { Entity entity = this.getType().create(worldserver); if (entity != null) { @@ -31,10 +31,10 @@ index 275184f437b7bdb6035fc468dc4396b915af1d50..593d5bed11d393f2094936d52d81b3e8 entity.moveTo(position.x, position.y, position.z, yaw, pitch); // Paper - use EntityPortalExitEvent values entity.setDeltaMovement(velocity); // Paper - use EntityPortalExitEvent values diff --git a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -index a16c549ee1715c24acb93d17da396355e16e9158..c8813fc7827d37896bc3c36f3de12bfbd62444f7 100644 +index c0274ed66bfe3a2bd8c67d0af807bc090dfabf7d..5ec7da98dc21ef1146d7d65667f3ecb4508f94ef 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java +++ b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -@@ -346,6 +346,23 @@ public class GaleWorldConfiguration extends ConfigurationPart { +@@ -344,6 +344,23 @@ public class GaleWorldConfiguration extends ConfigurationPart { } diff --git a/patches/server/0121-Don-t-load-chunks-to-spawn-phantoms.patch b/patches/server/0119-Don-t-load-chunks-to-spawn-phantoms.patch similarity index 91% rename from patches/server/0121-Don-t-load-chunks-to-spawn-phantoms.patch rename to patches/server/0119-Don-t-load-chunks-to-spawn-phantoms.patch index 90b5d66..7446385 100644 --- a/patches/server/0121-Don-t-load-chunks-to-spawn-phantoms.patch +++ b/patches/server/0119-Don-t-load-chunks-to-spawn-phantoms.patch @@ -13,10 +13,10 @@ As part of: MultiPaper (https://github.com/MultiPaper/MultiPaper) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java -index e8ae4449696d73c8c9b8b27d4d2e20db933a72cc..ffef23b9455613e8d9e4cc4e21e52b76603ee1bf 100644 +index bc7648dbc132551dc6591ab49a1919a623c30f60..11f6a1854c08cbf8ee5c99522c3cd2ae5322de6d 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java +++ b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java -@@ -73,7 +73,15 @@ public class PhantomSpawner implements CustomSpawner { +@@ -71,7 +71,15 @@ public class PhantomSpawner implements CustomSpawner { if (randomsource.nextInt(j) >= world.paperConfig().entities.behavior.playerInsomniaStartTicks) { // Paper BlockPos blockposition1 = blockposition.above(20 + randomsource.nextInt(15)).east(-10 + randomsource.nextInt(21)).south(-10 + randomsource.nextInt(21)); @@ -34,10 +34,10 @@ index e8ae4449696d73c8c9b8b27d4d2e20db933a72cc..ffef23b9455613e8d9e4cc4e21e52b76 if (NaturalSpawner.isValidEmptySpawnBlock(world, blockposition1, iblockdata, fluid, EntityType.PHANTOM)) { diff --git a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -index c8813fc7827d37896bc3c36f3de12bfbd62444f7..d99bed15c0dd58614c531469729a77ce53f6ac51 100644 +index 5ec7da98dc21ef1146d7d65667f3ecb4508f94ef..43b5b1e600d12b5ac39ee11cf3596c095cf4058d 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java +++ b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -@@ -272,6 +272,23 @@ public class GaleWorldConfiguration extends ConfigurationPart { +@@ -270,6 +270,23 @@ public class GaleWorldConfiguration extends ConfigurationPart { } diff --git a/patches/server/0122-Don-t-load-chunks-to-activate-climbing-entities.patch b/patches/server/0120-Don-t-load-chunks-to-activate-climbing-entities.patch similarity index 88% rename from patches/server/0122-Don-t-load-chunks-to-activate-climbing-entities.patch rename to patches/server/0120-Don-t-load-chunks-to-activate-climbing-entities.patch index 523e93c..8bf1d33 100644 --- a/patches/server/0122-Don-t-load-chunks-to-activate-climbing-entities.patch +++ b/patches/server/0120-Don-t-load-chunks-to-activate-climbing-entities.patch @@ -7,10 +7,10 @@ License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 593d5bed11d393f2094936d52d81b3e87a35fcf0..9e5f597c846cb033272f23ce1530002a2b41fb96 100644 +index a3243bba3a1afe23a2c4bf3ecb01f5fcf07837ce..74ec0729f052c6c3119e25a8891a8f7e6a77eaf5 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -4386,6 +4386,16 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -4519,6 +4519,16 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { return this.feetBlockState; } @@ -28,17 +28,17 @@ index 593d5bed11d393f2094936d52d81b3e87a35fcf0..9e5f597c846cb033272f23ce1530002a return this.chunkPosition; } diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 55489052b3472c43c71de3b5ec907f2526d81caf..9d5150aec55411abb5192f2e5cd59b3e1038a35c 100644 +index da2b72bdcd2ec7e1992a828c4d5a543b113a2592..c48b5750ebaa8852692ddb8b0873697361e885c9 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1943,19 +1943,43 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -1986,19 +1986,43 @@ public abstract class LivingEntity extends Entity implements Attackable { public boolean onClimbableCached() { if (!this.blockPosition().equals(this.lastClimbingPosition)) { - this.cachedOnClimbable = this.onClimbable(); - this.lastClimbingPosition = this.blockPosition(); + // Gale start - don't load chunks to activate climbing entities -+ Boolean onClimbableIfLoaded = this.onClimbable(this.level.galeConfig().smallOptimizations.loadChunks.toActivateClimbingEntities); ++ Boolean onClimbableIfLoaded = this.onClimbable(this.level().galeConfig().smallOptimizations.loadChunks.toActivateClimbingEntities); + if (onClimbableIfLoaded != null) { + this.cachedOnClimbable = onClimbableIfLoaded; + this.lastClimbingPosition = this.blockPosition(); @@ -79,10 +79,10 @@ index 55489052b3472c43c71de3b5ec907f2526d81caf..9d5150aec55411abb5192f2e5cd59b3e if (iblockdata.is(BlockTags.CLIMBABLE)) { this.lastClimbablePos = Optional.of(blockposition); diff --git a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -index d99bed15c0dd58614c531469729a77ce53f6ac51..bf40c19d3449742ea1f08714f4d8096c6152108e 100644 +index 43b5b1e600d12b5ac39ee11cf3596c095cf4058d..e47a06ecd6516a78eba59a043e4aa460fa3b9375 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java +++ b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -@@ -287,6 +287,18 @@ public class GaleWorldConfiguration extends ConfigurationPart { +@@ -285,6 +285,18 @@ public class GaleWorldConfiguration extends ConfigurationPart { public boolean toSpawnPhantoms = false; // Gale end - MultiPaper - don't load chunks to spawn phantoms diff --git a/patches/server/0123-Broadcast-crit-animations-as-the-entity-being-critte.patch b/patches/server/0121-Broadcast-crit-animations-as-the-entity-being-critte.patch similarity index 85% rename from patches/server/0123-Broadcast-crit-animations-as-the-entity-being-critte.patch rename to patches/server/0121-Broadcast-crit-animations-as-the-entity-being-critte.patch index 435626f..75753e3 100644 --- a/patches/server/0123-Broadcast-crit-animations-as-the-entity-being-critte.patch +++ b/patches/server/0121-Broadcast-crit-animations-as-the-entity-being-critte.patch @@ -13,35 +13,35 @@ As part of: MultiPaper (https://github.com/MultiPaper/MultiPaper) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 9de7eda75e41065ab509c05c3ed844d943ccfe00..2cfa9b6568ab15dcde884b7d1b5d738c7ceb8210 100644 +index e7ec98be67ab50c06d90a1bccd023fcd2a2fee93..6bbb894b26c2ed3844aa5a6ea809fea74538468a 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1863,12 +1863,18 @@ public class ServerPlayer extends Player { +@@ -1908,12 +1908,18 @@ public class ServerPlayer extends Player { @Override public void crit(Entity target) { -- this.getLevel().getChunkSource().broadcastAndSend(this, new ClientboundAnimatePacket(target, 4)); +- this.serverLevel().getChunkSource().broadcastAndSend(this, new ClientboundAnimatePacket(target, 4)); + // Gale start - MultiPaper - broadcast crit animations as the entity being critted -+ var level = this.getLevel(); ++ var level = this.serverLevel(); + level.getChunkSource().broadcastAndSend(level.galeConfig().gameplayMechanics.fixes.broadcastCritAnimationsAsTheEntityBeingCritted ? target : this, new ClientboundAnimatePacket(target, 4)); + // Gale end - MultiPaper - broadcast crit animations as the entity being critted } @Override public void magicCrit(Entity target) { -- this.getLevel().getChunkSource().broadcastAndSend(this, new ClientboundAnimatePacket(target, 5)); +- this.serverLevel().getChunkSource().broadcastAndSend(this, new ClientboundAnimatePacket(target, 5)); + // Gale start - MultiPaper - broadcast crit animations as the entity being critted -+ var level = this.getLevel(); ++ var level = this.serverLevel(); + level.getChunkSource().broadcastAndSend(level.galeConfig().gameplayMechanics.fixes.broadcastCritAnimationsAsTheEntityBeingCritted ? target : this, new ClientboundAnimatePacket(target, 5)); + // Gale end - MultiPaper - broadcast crit animations as the entity being critted } @Override diff --git a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -index bf40c19d3449742ea1f08714f4d8096c6152108e..8d60dde978dcfdc0acc89420443325843a892ea0 100644 +index e47a06ecd6516a78eba59a043e4aa460fa3b9375..16b464573b6ef9d2995e475edb4a8b569c99103d 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java +++ b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -@@ -348,6 +348,22 @@ public class GaleWorldConfiguration extends ConfigurationPart { +@@ -346,6 +346,22 @@ public class GaleWorldConfiguration extends ConfigurationPart { public boolean keepMooshroomRotationAfterShearing = true; // Gale - Purpur - fix cow rotation when shearing mooshroom public boolean checkCanChangeDimensionsBeforeUseEndGateway = false; // Gale - Purpur - end gateway should check if entity can use portal diff --git a/patches/server/0124-Ignore-null-legacy-structure-data.patch b/patches/server/0122-Ignore-null-legacy-structure-data.patch similarity index 97% rename from patches/server/0124-Ignore-null-legacy-structure-data.patch rename to patches/server/0122-Ignore-null-legacy-structure-data.patch index e07a53e..b7424d0 100644 --- a/patches/server/0124-Ignore-null-legacy-structure-data.patch +++ b/patches/server/0122-Ignore-null-legacy-structure-data.patch @@ -13,7 +13,7 @@ As part of: MultiPaper (https://github.com/MultiPaper/MultiPaper) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java b/src/main/java/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java -index 3b2df0411f9ef85445a53292a65f805a80218979..99126888d4ccae266e55ed3d9ed858fb9ac80727 100644 +index cc7222cc7e53e8ae693e4e94ad53391db7a663c4..9d1d7033fdd2ae65b8fd323e9199b9d5ea43fe35 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java +++ b/src/main/java/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java @@ -19,6 +19,7 @@ import net.minecraft.resources.ResourceKey; diff --git a/patches/server/0125-Don-t-double-save-stored-user-lists.patch b/patches/server/0123-Don-t-double-save-stored-user-lists.patch similarity index 100% rename from patches/server/0125-Don-t-double-save-stored-user-lists.patch rename to patches/server/0123-Don-t-double-save-stored-user-lists.patch diff --git a/patches/server/0126-Skip-unnecessary-mob-spawning-computations.patch b/patches/server/0124-Skip-unnecessary-mob-spawning-computations.patch similarity index 92% rename from patches/server/0126-Skip-unnecessary-mob-spawning-computations.patch rename to patches/server/0124-Skip-unnecessary-mob-spawning-computations.patch index 30595e4..69a46c4 100644 --- a/patches/server/0126-Skip-unnecessary-mob-spawning-computations.patch +++ b/patches/server/0124-Skip-unnecessary-mob-spawning-computations.patch @@ -13,10 +13,10 @@ As part of: MultiPaper (https://github.com/MultiPaper/MultiPaper) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 8ed18a158a13dceca7cd342125c09e52ac0d013c..df4db98618c6c9261b4ec8e2987c4ed26af4bd4b 100644 +index 041f188b742ad819e2460e8ca5ab7f44412dbf9b..3c9ff57cf8cf7e7bfca234e460ff869165bd40d3 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -693,10 +693,15 @@ public class ServerChunkCache extends ChunkSource { +@@ -543,10 +543,15 @@ public class ServerChunkCache extends ChunkSource { int k = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); boolean flag1 = level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && worlddata.getGameTime() % level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit @@ -33,7 +33,7 @@ index 8ed18a158a13dceca7cd342125c09e52ac0d013c..df4db98618c6c9261b4ec8e2987c4ed2 if ((this.spawnFriendlies || this.spawnEnemies) && this.chunkMap.playerMobDistanceMap != null) { // don't count mobs when animals and monsters are disabled // re-set mob counts for (ServerPlayer player : this.level.players) { -@@ -710,13 +715,16 @@ public class ServerChunkCache extends ChunkSource { +@@ -560,13 +565,16 @@ public class ServerChunkCache extends ChunkSource { this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings this.lastSpawnState = spawnercreature_d; @@ -51,8 +51,8 @@ index 8ed18a158a13dceca7cd342125c09e52ac0d013c..df4db98618c6c9261b4ec8e2987c4ed2 - // Paper - only shuffle if per-player mob spawning is disabled // Paper - moved natural spawn event up - // Paper start - optimise chunk tick iteration -@@ -745,7 +753,7 @@ public class ServerChunkCache extends ChunkSource { + +@@ -596,7 +604,7 @@ public class ServerChunkCache extends ChunkSource { if ((true || this.level.isNaturalSpawningAllowed(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning // Paper - the chunk is known ticking chunk1.incrementInhabitedTime(j); @@ -61,7 +61,7 @@ index 8ed18a158a13dceca7cd342125c09e52ac0d013c..df4db98618c6c9261b4ec8e2987c4ed2 NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1); } -@@ -806,6 +814,20 @@ public class ServerChunkCache extends ChunkSource { +@@ -657,6 +665,20 @@ public class ServerChunkCache extends ChunkSource { } } diff --git a/patches/server/0127-Prevent-entities-random-strolling-into-non-ticking-c.patch b/patches/server/0125-Prevent-entities-random-strolling-into-non-ticking-c.patch similarity index 81% rename from patches/server/0127-Prevent-entities-random-strolling-into-non-ticking-c.patch rename to patches/server/0125-Prevent-entities-random-strolling-into-non-ticking-c.patch index 9f8e153..4686ffb 100644 --- a/patches/server/0127-Prevent-entities-random-strolling-into-non-ticking-c.patch +++ b/patches/server/0125-Prevent-entities-random-strolling-into-non-ticking-c.patch @@ -13,7 +13,7 @@ As part of: MultiPaper (https://github.com/MultiPaper/MultiPaper) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/RandomStrollGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/RandomStrollGoal.java -index 216929c838446c3c14d9b9906ffa625ef35fcbc8..f9e0cf073abf3e0ffcedd4418a66c310e057d368 100644 +index 216929c838446c3c14d9b9906ffa625ef35fcbc8..39035605197bad2eb2fd0c4942c5d43bfdb677f3 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/RandomStrollGoal.java +++ b/src/main/java/net/minecraft/world/entity/ai/goal/RandomStrollGoal.java @@ -2,6 +2,9 @@ package net.minecraft.world.entity.ai.goal; @@ -31,15 +31,15 @@ index 216929c838446c3c14d9b9906ffa625ef35fcbc8..f9e0cf073abf3e0ffcedd4418a66c310 Vec3 vec3 = this.getPosition(); - if (vec3 == null) { -+ if (vec3 == null || (!this.mob.level.galeConfig().gameplayMechanics.entitiesCanRandomStrollIntoNonTickingChunks && !((ServerLevel) this.mob.level).isPositionEntityTicking(BlockPos.containing(vec3)))) { // Gale - MultiPaper - prevent entities random strolling into non-ticking chunks ++ if (vec3 == null || (!this.mob.level().galeConfig().gameplayMechanics.entitiesCanRandomStrollIntoNonTickingChunks && !((ServerLevel) this.mob.level()).isPositionEntityTicking(BlockPos.containing(vec3)))) { // Gale - MultiPaper - prevent entities random strolling into non-ticking chunks return false; } else { this.wantedX = vec3.x; diff --git a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -index 8d60dde978dcfdc0acc89420443325843a892ea0..0b4621b60c2eee7201b38016063ec3db11706022 100644 +index 16b464573b6ef9d2995e475edb4a8b569c99103d..c03a6650d65ada6014019b09b29dc4d4f9b21286 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java +++ b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java -@@ -324,6 +324,7 @@ public class GaleWorldConfiguration extends ConfigurationPart { +@@ -322,6 +322,7 @@ public class GaleWorldConfiguration extends ConfigurationPart { // Gale end - variable entity wake-up duration public boolean entitiesCanEatBlocksInNonTickingChunks = false; // Gale - Purpur - prevent entities eating blocks in non-ticking chunks diff --git a/patches/server/0128-Do-not-place-player-in-world-if-kicked-before-being-.patch b/patches/server/0126-Do-not-place-player-in-world-if-kicked-before-being-.patch similarity index 92% rename from patches/server/0128-Do-not-place-player-in-world-if-kicked-before-being-.patch rename to patches/server/0126-Do-not-place-player-in-world-if-kicked-before-being-.patch index 2de9cc9..9a8521a 100644 --- a/patches/server/0128-Do-not-place-player-in-world-if-kicked-before-being-.patch +++ b/patches/server/0126-Do-not-place-player-in-world-if-kicked-before-being-.patch @@ -14,10 +14,10 @@ As part of: MultiPaper (https://github.com/MultiPaper/MultiPaper) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index f87d10098ea3e3c35e574dc388da6eba4bc6422c..eb90b375c589076afc454bd80f013384df090b11 100644 +index 1678492a2e65bea22e9c10d6680218160dda2c2d..88d350ecfb16e34d6710ad7b17ce7438e4dbceb9 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -262,6 +262,13 @@ public abstract class PlayerList { +@@ -269,6 +269,13 @@ public abstract class PlayerList { org.spigotmc.event.player.PlayerSpawnLocationEvent ev = new com.destroystokyo.paper.event.player.PlayerInitialSpawnEvent(spawnPlayer, spawnPlayer.getLocation()); // Paper use our duplicate event this.cserver.getPluginManager().callEvent(ev); diff --git a/patches/server/0129-CraftBukkit-UUID-to-world-map.patch b/patches/server/0127-CraftBukkit-UUID-to-world-map.patch similarity index 97% rename from patches/server/0129-CraftBukkit-UUID-to-world-map.patch rename to patches/server/0127-CraftBukkit-UUID-to-world-map.patch index 97f70df..e150586 100644 --- a/patches/server/0129-CraftBukkit-UUID-to-world-map.patch +++ b/patches/server/0127-CraftBukkit-UUID-to-world-map.patch @@ -13,7 +13,7 @@ As part of: MultiPaper (https://github.com/MultiPaper/MultiPaper) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 23438408f99d11522d4559900dc67566bc662587..877b4a813c6d781635d8647dbb18f8445cd88a6e 100644 +index e9412757b1b8cdee878b5a210ad7d9b008e9d710..be250f95e1ebf61e74b3c025f2c90d48c75aad76 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -42,6 +42,9 @@ import java.util.logging.Level; diff --git a/patches/server/0130-Global-EULA-file.patch b/patches/server/0128-Global-EULA-file.patch similarity index 100% rename from patches/server/0130-Global-EULA-file.patch rename to patches/server/0128-Global-EULA-file.patch diff --git a/patches/server/0131-Specific-interval-TPS-API.patch b/patches/server/0129-Specific-interval-TPS-API.patch similarity index 88% rename from patches/server/0131-Specific-interval-TPS-API.patch rename to patches/server/0129-Specific-interval-TPS-API.patch index 8b0d5b3..a71e4a6 100644 --- a/patches/server/0131-Specific-interval-TPS-API.patch +++ b/patches/server/0129-Specific-interval-TPS-API.patch @@ -7,10 +7,10 @@ License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 877b4a813c6d781635d8647dbb18f8445cd88a6e..dde2bf7241be692054ab3f78e5f3d19cc2c55929 100644 +index be250f95e1ebf61e74b3c025f2c90d48c75aad76..8ff6771e6ce1d8b9929aaf220e8eaee2243218c6 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -2732,6 +2732,25 @@ public final class CraftServer implements Server { +@@ -2728,6 +2728,25 @@ public final class CraftServer implements Server { }; } diff --git a/patches/server/0132-5-second-TPS-average.patch b/patches/server/0130-5-second-TPS-average.patch similarity index 95% rename from patches/server/0132-5-second-TPS-average.patch rename to patches/server/0130-5-second-TPS-average.patch index 43dced4..f52dcf3 100644 --- a/patches/server/0132-5-second-TPS-average.patch +++ b/patches/server/0130-5-second-TPS-average.patch @@ -59,10 +59,10 @@ index fa56cd09102a89692b42f1d14257990508c5c720..fac07dda413002c12276131efbe4ee38 setListData(vector); } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 71b4de55621ef7bb2df4fbfc611ce32023cdd628..b298c9715fed604dab25e9d336806acf110f822a 100644 +index 850f2299595e081cbac459304e8c52d94d90f6f4..6f7b2e788cd9e0e74f74a83706d30ab98a032507 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1037,6 +1037,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 1e-6 || oldDeltaMovement.z < -1e-6 || oldDeltaMovement.z > 1e-6) { + // Gale end - skip negligible planar movement multiplication - float f2 = this.getBlockSpeedFactor(); + float f = this.getBlockSpeedFactor(); -- this.setDeltaMovement(this.getDeltaMovement().multiply((double) f2, 1.0D, (double) f2)); +- this.setDeltaMovement(this.getDeltaMovement().multiply((double) f, 1.0D, (double) f)); + // Gale start - skip negligible planar movement multiplication -+ if (f2 < 1 - 1e-6 || f2 > 1 + 1e-6) { -+ this.setDeltaMovement(oldDeltaMovement.multiply(f2, 1.0D, f2)); ++ if (f < 1 - 1e-6 || f > 1 + 1e-6) { ++ this.setDeltaMovement(oldDeltaMovement.multiply((double) f, 1.0D, (double) f)); + } + } + // Gale end - skip negligible planar movement multiplication diff --git a/patches/server/0137-Optimize-matching-item-checks.patch b/patches/server/0137-Optimize-matching-item-checks.patch new file mode 100644 index 0000000..39e3ada --- /dev/null +++ b/patches/server/0137-Optimize-matching-item-checks.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martijn Muijsers +Date: Sun, 25 Dec 2022 20:51:32 +0100 +Subject: [PATCH] Optimize matching item checks + +License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) +Gale - https://galemc.org + +diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java +index 7ca7834a1a0593a6d1eb105e215784100076d421..6af4ae47a357d09ba9cba8248d5bd0da90b25916 100644 +--- a/src/main/java/net/minecraft/world/item/ItemStack.java ++++ b/src/main/java/net/minecraft/world/item/ItemStack.java +@@ -749,15 +749,15 @@ public final class ItemStack { + } + + public static boolean matches(ItemStack left, ItemStack right) { +- return left == right ? true : (left.getCount() != right.getCount() ? false : ItemStack.isSameItemSameTags(left, right)); ++ return left == right || (left.getCount() == right.getCount() && ItemStack.isSameItemSameTags(left, right)); // Gale - optimize identical item checks + } + + public static boolean isSameItem(ItemStack left, ItemStack right) { +- return left.is(right.getItem()); ++ return left == right || left.is(right.getItem()); // Gale - optimize identical item checks + } + + public static boolean isSameItemSameTags(ItemStack stack, ItemStack otherStack) { +- return !stack.is(otherStack.getItem()) ? false : (stack.isEmpty() && otherStack.isEmpty() ? true : Objects.equals(stack.tag, otherStack.tag)); ++ return stack == otherStack || (stack.is(otherStack.getItem()) && (stack.isEmpty() && otherStack.isEmpty() || Objects.equals(stack.tag, otherStack.tag))); // Gale - optimize identical item checks + } + + public String getDescriptionId() { diff --git a/patches/server/0140-Reduce-RandomSource-instances.patch b/patches/server/0138-Reduce-RandomSource-instances.patch similarity index 82% rename from patches/server/0140-Reduce-RandomSource-instances.patch rename to patches/server/0138-Reduce-RandomSource-instances.patch index da45bf4..9c791ec 100644 --- a/patches/server/0140-Reduce-RandomSource-instances.patch +++ b/patches/server/0138-Reduce-RandomSource-instances.patch @@ -13,7 +13,7 @@ As part of: Patina (https://github.com/PatinaMC/Patina) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/net/minecraft/server/commands/SpreadPlayersCommand.java b/src/main/java/net/minecraft/server/commands/SpreadPlayersCommand.java -index 591163d8f8300b084ac734800efee902c4def958..387c390966ddbc48aa7333f7199b792755ffcd04 100644 +index 1af5f4fde0d9d6c7d9dd32714a4e31b298d7564d..098828871f36ef8f9796456747977d7c9f02c9a3 100644 --- a/src/main/java/net/minecraft/server/commands/SpreadPlayersCommand.java +++ b/src/main/java/net/minecraft/server/commands/SpreadPlayersCommand.java @@ -65,7 +65,7 @@ public class SpreadPlayersCommand { @@ -26,10 +26,10 @@ index 591163d8f8300b084ac734800efee902c4def958..387c390966ddbc48aa7333f7199b7927 double d1 = (double) (center.y - maxRange); double d2 = (double) (center.x + maxRange); diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 2cfa9b6568ab15dcde884b7d1b5d738c7ceb8210..4ea43001ecbaa37b60b42b1a90b14f2ffac62f36 100644 +index 6bbb894b26c2ed3844aa5a6ea809fea74538468a..f4cc145f2670dee5893eeb1891b3fb8bfadd9b36 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -400,7 +400,7 @@ public class ServerPlayer extends Player { +@@ -441,7 +441,7 @@ public class ServerPlayer extends Player { long l = k * k; int i1 = l > 2147483647L ? Integer.MAX_VALUE : (int) l; int j1 = this.getCoprime(i1); @@ -38,7 +38,7 @@ index 2cfa9b6568ab15dcde884b7d1b5d738c7ceb8210..4ea43001ecbaa37b60b42b1a90b14f2f for (int l1 = 0; l1 < i1; ++l1) { int i2 = (k1 + j1 * l1) % i1; -@@ -437,7 +437,7 @@ public class ServerPlayer extends Player { +@@ -478,7 +478,7 @@ public class ServerPlayer extends Player { long l = k * k; int i1 = l > 2147483647L ? Integer.MAX_VALUE : (int) l; int j1 = this.getCoprime(i1); @@ -61,7 +61,7 @@ index 1ef089dbf83de35d875c00efdf468c397be56978..b9f5dc95f859acb8f8fd4739537485af } diff --git a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java -index bcb0f3fd09ed064b64dc6495302b40828d906837..65262759862dde8e720c032530859b6db8786657 100644 +index b8c238287e0639b578170c6fec0d4db5a1a59fe7..054c68780c0ac8e0f64dcc832d5d272e75bc2d86 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java +++ b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java @@ -86,7 +86,7 @@ public class FishingHook extends Projectile { @@ -74,7 +74,7 @@ index bcb0f3fd09ed064b64dc6495302b40828d906837..65262759862dde8e720c032530859b6d this.currentState = FishingHook.FishHookState.FLYING; this.noCulling = true; diff --git a/src/main/java/net/minecraft/world/entity/raid/Raid.java b/src/main/java/net/minecraft/world/entity/raid/Raid.java -index 246584452eb01b168d2372e1ccc4b357b63891df..9d8ed30c7fac6e72d9678ca163189b93eaa4f3ff 100644 +index 0db3cbcdbc7f73dd2c10bfb696209753fa33aa8d..5d8968d2c22d2548ecb1b5dde0ead600b1a83735 100644 --- a/src/main/java/net/minecraft/world/entity/raid/Raid.java +++ b/src/main/java/net/minecraft/world/entity/raid/Raid.java @@ -111,7 +111,7 @@ public class Raid { @@ -96,7 +96,7 @@ index 246584452eb01b168d2372e1ccc4b357b63891df..9d8ed30c7fac6e72d9678ca163189b93 this.level = world; this.id = nbt.getInt("Id"); diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 298689244d9d66d197b2b7728475084546219255..c321215a39cf9f69e90c2c6e139963f60877286b 100644 +index 8f2c0dcb0ca69ea1b09bb055c6a9494980be8e92..2a7813209d664fd8c123488ce9c530b4440be163 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java @@ -81,7 +81,7 @@ public class Explosion { @@ -109,7 +109,7 @@ index 298689244d9d66d197b2b7728475084546219255..c321215a39cf9f69e90c2c6e139963f6 this.hitPlayers = Maps.newHashMap(); this.level = world; diff --git a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java -index ad566af22e58b4df9e6380b009eaced4a0efdc76..97a21c3614830618f320c50461a2a84d90a7f285 100644 +index 414a47a82927bed02a4f3cf330c78e46f1d66782..cc17cb985856fd8d1083b5b1f57f82c651d28443 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java @@ -361,7 +361,7 @@ public class TheEndGatewayBlockEntity extends TheEndPortalBlockEntity { @@ -122,10 +122,10 @@ index ad566af22e58b4df9e6380b009eaced4a0efdc76..97a21c3614830618f320c50461a2a84d @Override diff --git a/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java b/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java -index e9eb32469a5c03f7a3677ef50fd4541c1ed662ad..dc0eb8eb2bf39e33bdee4daa3647510d24535091 100644 +index e2cd77d9de9709aa50f4b6febabe21bfcf94dc5d..f04699f87e1a52b7349fe776de44af0c5fa01343 100644 --- a/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java +++ b/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java -@@ -411,7 +411,7 @@ public class EndDragonFight { +@@ -402,7 +402,7 @@ public class EndDragonFight { this.level.registryAccess().registry(Registries.CONFIGURED_FEATURE).flatMap((registry) -> { return registry.getHolder(EndFeatures.END_GATEWAY_DELAYED); }).ifPresent((reference) -> { @@ -134,7 +134,7 @@ index e9eb32469a5c03f7a3677ef50fd4541c1ed662ad..dc0eb8eb2bf39e33bdee4daa3647510d }); } -@@ -427,7 +427,7 @@ public class EndDragonFight { +@@ -418,7 +418,7 @@ public class EndDragonFight { this.portalLocation = this.portalLocation.atY(this.level.getMinBuildHeight() + 1); } // Paper end @@ -143,16 +143,3 @@ index e9eb32469a5c03f7a3677ef50fd4541c1ed662ad..dc0eb8eb2bf39e33bdee4daa3647510d } @Nullable -diff --git a/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java b/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java -index eb399244af70c8fe735657d429d883c48215af0a..e78a8d68c4af056af9e1452f76651fea545189ba 100644 ---- a/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java -+++ b/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java -@@ -202,7 +202,7 @@ public class LootContext { - } else { - RandomSource randomSource = this.random; - if (randomSource == null) { -- randomSource = RandomSource.create(); -+ randomSource = level.random; // Gale - Patina - reduce RandomSource instances - } - - MinecraftServer minecraftServer = this.level.getServer(); diff --git a/patches/server/0139-Optimize-identical-item-checks.patch b/patches/server/0139-Optimize-identical-item-checks.patch deleted file mode 100644 index 507a20c..0000000 --- a/patches/server/0139-Optimize-identical-item-checks.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Sun, 25 Dec 2022 20:51:32 +0100 -Subject: [PATCH] Optimize identical item checks - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index f2c3d0913ee37e4f2d5f409f47581be9d8b2fa3d..e501450728e72c7e69a9009a26efbb813ff9ff42 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -733,14 +733,29 @@ public final class ItemStack { - } - - public static boolean tagMatches(ItemStack left, ItemStack right) { -+ // Gale start - optimize identical item checks -+ if (left == right) { -+ return true; -+ } -+ // Gale end - optimize identical item checks - return left.isEmpty() && right.isEmpty() ? true : (!left.isEmpty() && !right.isEmpty() ? (left.tag == null && right.tag != null ? false : left.tag == null || left.tag.equals(right.tag)) : false); - } - - public static boolean matches(ItemStack left, ItemStack right) { -+ // Gale start - optimize identical item checks -+ if (left == right) { -+ return true; -+ } -+ // Gale end - optimize identical item checks - return left.isEmpty() && right.isEmpty() ? true : (!left.isEmpty() && !right.isEmpty() ? left.matches(right) : false); - } - - private boolean matches(ItemStack stack) { -+ // Gale start - optimize identical item checks -+ if (this == stack) { -+ return true; -+ } -+ // Gale end - optimize identical item checks - return this.count != stack.count ? false : (!this.is(stack.getItem()) ? false : (this.tag == null && stack.tag != null ? false : this.tag == null || this.tag.equals(stack.tag))); - } - diff --git a/patches/server/0141-Server-thread-priority-environment-variable.patch b/patches/server/0139-Server-thread-priority-environment-variable.patch similarity index 87% rename from patches/server/0141-Server-thread-priority-environment-variable.patch rename to patches/server/0139-Server-thread-priority-environment-variable.patch index ae167cc..8cfefee 100644 --- a/patches/server/0141-Server-thread-priority-environment-variable.patch +++ b/patches/server/0139-Server-thread-priority-environment-variable.patch @@ -7,10 +7,10 @@ License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index df7e5ac7f56f31f3feac0096013f49c7948d5104..9a7ef74ad82b1de6e386994dea0d99a9240ad7af 100644 +index b0a7dfeb1d2bae669410ffcce256645ab314b465..ecb42a8f9a8eb18d7fd3f828ece4ac8ebf4de0e0 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -194,6 +194,8 @@ import co.aikar.timings.MinecraftTimings; // Paper +@@ -186,6 +186,8 @@ import co.aikar.timings.MinecraftTimings; // Paper public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements CommandSource, AutoCloseable { @@ -19,7 +19,7 @@ index df7e5ac7f56f31f3feac0096013f49c7948d5104..9a7ef74ad82b1de6e386994dea0d99a9 private static MinecraftServer SERVER; // Paper public static final Logger LOGGER = LogUtils.getLogger(); public static final net.kyori.adventure.text.logger.slf4j.ComponentLogger COMPONENT_LOGGER = net.kyori.adventure.text.logger.slf4j.ComponentLogger.logger(LOGGER.getName()); // Paper -@@ -321,7 +323,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop -Date: Sun, 4 Dec 2022 14:42:26 +0100 -Subject: [PATCH] Thread-safety annotations - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/org/galemc/gale/executor/annotation/Access.java b/src/main/java/org/galemc/gale/executor/annotation/Access.java -new file mode 100644 -index 0000000000000000000000000000000000000000..50541414e1d91ff06d108d9b3fe64dcb4ad09668 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/annotation/Access.java -@@ -0,0 +1,39 @@ -+// Gale - thread-safety annotations -+ -+package org.galemc.gale.executor.annotation; -+ -+/** -+ * An enum to distinguish the type of field access that a thread-safety annotation describes. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public enum Access { -+ -+ /** -+ * If the annotation is applied to a field, it holds for access to the field's value. -+ *
-+ * This may or may not extend to conceptual access rather than field value access: for instance, if the field is -+ * a list, accessing elements of the list, or if the field is an object reference, -+ * accessing field of that reference. -+ */ -+ READ, -+ /** -+ * If the annotation is applied to a field, it holds for modifications made to the field. -+ *
-+ * This may or may not extend to conceptual modifications rather than setting the field value: for instance, -+ * if the field is a list, adding elements to the list, or if the field is an object reference, -+ * setting fields of that reference. -+ */ -+ WRITE, -+ /** -+ * Both {@link #READ} and {@link #WRITE}: if the annotation is applied to a field, it holds for both access to -+ * the field's value, and for modifications made to the field. -+ *
-+ * This may or may not extend to conceptual access and/or modifications. -+ * -+ * @see #READ -+ * @see #WRITE -+ */ -+ READ_WRITE -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/annotation/Guarded.java b/src/main/java/org/galemc/gale/executor/annotation/Guarded.java -new file mode 100644 -index 0000000000000000000000000000000000000000..6f1d1960953daf7f6f61643f5165e9a0760a647e ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/annotation/Guarded.java -@@ -0,0 +1,55 @@ -+// Gale - thread-safety annotations -+ -+package org.galemc.gale.executor.annotation; -+ -+import org.galemc.gale.executor.annotation.thread.ThreadRestricted; -+ -+import java.lang.annotation.Documented; -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Repeatable; -+import java.lang.annotation.Target; -+ -+/** -+ * An annotation primarily for fields, identifying fields that can only be accessed and/or modified when the -+ * described lock is acquired by the current thread. -+ *
-+ * When applied to a method, this annotation indicates that calls to the method must happen only when the described -+ * lock is acquired by the current thread. This annotation does NOT mean that the method will acquire the lock -+ * in its method body. -+ *
-+ * When applied to a class, this annotation indicates it holds for all methods and fields, both instance and static, -+ * belonging to the class, or any superclass thereof, or any inner or statically nested class of the class or -+ * any superclass thereof, except for any cases where a conflicting annotation with a smaller scope is also present. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@Documented -+@Repeatable(Guarded.Container.class) -+@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) -+public @interface Guarded { -+ -+ /** -+ * @return A description of the lock that the annotated element is guarded by: typically a Javadoc reference -+ * to a field, e.g. "#statusLock". -+ */ -+ String value(); -+ -+ /** -+ * @see ThreadRestricted#fieldAccess() -+ */ -+ Access fieldAccess() default Access.READ_WRITE; -+ -+ /** -+ * @return A description of in what scenario the lock described by this annotation is not needed to be acquired. -+ */ -+ String except() default ""; -+ -+ @Documented -+ @Target(ElementType.ANNOTATION_TYPE) -+ @interface Container { -+ -+ Guarded[] value(); -+ -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a4dc0ebe48fdd352387f06be42ff46fc11ee5822 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java -@@ -0,0 +1,34 @@ -+// Gale - thread-safety annotations -+ -+package org.galemc.gale.executor.annotation; -+ -+import java.lang.annotation.Documented; -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Target; -+ -+/** -+ * An annotation primarily for methods, identifying methods that may block the thread. -+ *
-+ * When applied to a class, this annotation indicates it holds for all methods, both instance and static, -+ * belonging to the class, or any superclass thereof, or any inner or statically nested class of the class or -+ * any superclass thereof, except for any cases where a conflicting annotation with a smaller scope is also present. -+ *
-+ * In a method annotated with {@link PotentiallyBlocking}, fields and methods annotated with -+ * {@link PotentiallyBlocking}, {@link PotentiallyYielding} or {@link YieldFree} may all be used. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@Documented -+@Target({ElementType.METHOD, ElementType.TYPE}) -+public @interface PotentiallyBlocking { -+ -+ /** -+ * @return A description of when the described method is potentially blocking. -+ * When describing conditions, the description should make clear whether those conditions must hold for the method -+ * to block, or make blocking more likely, or will certainly lead the method to block, or preclude the method -+ * from blocking, or have some other relation to the expectation of blocking. -+ * If left empty (as default), the method potentially blocks under any conditions. -+ */ -+ String value() default ""; -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java -new file mode 100644 -index 0000000000000000000000000000000000000000..44b70d68ba6823ab72ea9af4b7774051785c0a2b ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java -@@ -0,0 +1,35 @@ -+// Gale - thread-safety annotations -+ -+package org.galemc.gale.executor.annotation; -+ -+import java.lang.annotation.Documented; -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Target; -+ -+/** -+ * An annotation primarily for methods, identifying methods that do not block, but may yield to other tasks -+ * under certain circumstances. -+ *
-+ * When applied to a class, this annotation indicates it holds for all methods, both instance and static, -+ * belonging to the class, or any superclass thereof, or any inner or statically nested class of the class or -+ * any superclass thereof, except for any cases where a conflicting annotation with a smaller scope is also present. -+ *
-+ * In a method annotated with {@link PotentiallyYielding}, the only methods that can be called are those -+ * annotated with {@link PotentiallyYielding} or {@link YieldFree}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@Documented -+@Target({ElementType.METHOD, ElementType.TYPE}) -+public @interface PotentiallyYielding { -+ -+ /** -+ * @return A description of when the described method is potentially yielding. -+ * When describing conditions, the description should make clear whether those conditions must hold for the method -+ * to yield, or make yielding more likely, or will certainly lead the method to yield, or preclude the method -+ * from yielding, or have some other relation to the expectation of yielding. -+ * If left empty (as default), the method potentially yields under any conditions. -+ */ -+ String value() default ""; -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/annotation/YieldFree.java b/src/main/java/org/galemc/gale/executor/annotation/YieldFree.java -new file mode 100644 -index 0000000000000000000000000000000000000000..dc660a3fb401fc3cb713a4556468fd6686e29c51 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/annotation/YieldFree.java -@@ -0,0 +1,27 @@ -+// Gale - thread-safety annotations -+ -+package org.galemc.gale.executor.annotation; -+ -+import java.lang.annotation.Documented; -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Target; -+import java.util.concurrent.locks.Lock; -+ -+/** -+ * An annotation primarily for methods, identifying methods that do not block, and are yield-free, i.e. that never -+ * yield to other tasks. Such a method may still speculatively attempt to acquire blocking locks or yielding locks, -+ * e.g. using {@link Lock#tryLock}, under the condition that failure to acquire the lock does not cause blocking -+ * or yielding. -+ *
-+ * When applied to a class, this annotation indicates it holds for all methods, both instance and static, -+ * belonging to the class, or any superclass thereof, or any inner or statically nested class of the class or -+ * any superclass thereof, except for any cases where a conflicting annotation with a smaller scope is also present. -+ *
-+ * In a method annotated with {@link PotentiallyYielding}, the only methods that can be called are those -+ * annotated with {@link YieldFree}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@Documented -+@Target({ElementType.METHOD, ElementType.TYPE}) -+public @interface YieldFree {} -diff --git a/src/main/java/org/galemc/gale/executor/annotation/thread/AnyThreadSafe.java b/src/main/java/org/galemc/gale/executor/annotation/thread/AnyThreadSafe.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f9f4deeb3edce30a8052172c0b2be905bbd90844 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/annotation/thread/AnyThreadSafe.java -@@ -0,0 +1,42 @@ -+// Gale - thread-safety annotations -+ -+package org.galemc.gale.executor.annotation.thread; -+ -+import org.galemc.gale.executor.annotation.Access; -+import org.galemc.gale.executor.annotation.PotentiallyBlocking; -+import org.galemc.gale.executor.annotation.PotentiallyYielding; -+ -+import java.lang.annotation.Documented; -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Target; -+ -+/** -+ * An annotation primarily for methods, identifying methods that can safely called from any thread, -+ * including but not limited to threads that are yielding. -+ *
-+ * When applied to a field, this annotation indicates that the field can be safely arbitrarily accessed and/or modified -+ * from any thread. -+ *
-+ * When applied to a class, this annotation indicates it holds for all methods and fields, both instance and static, -+ * belonging to the class, or any superclass thereof, or any inner or statically nested class of the class or -+ * any superclass thereof, except for any cases where a conflicting annotation with a smaller scope is also present. -+ *
-+ * In a method annotated with {@link AnyThreadSafe}, the only fields and methods that can be used are those -+ * annotated with {@link AnyThreadSafe}, unless extra safety guarantees are checked or explicitly provided. -+ * Use of methods that are potentially yielding (at least those annotated with {@link PotentiallyYielding}, but also -+ * those annotated with {@link PotentiallyBlocking} that potentially yield) is discouraged, -+ * because some threads cannot yield, and must block instead, -+ * leading to context switches where none may be expected. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@Documented -+@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD}) -+public @interface AnyThreadSafe { -+ -+ /** -+ * @see ThreadRestricted#fieldAccess() -+ */ -+ Access value() default Access.READ_WRITE; -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/annotation/thread/ThisThreadOnly.java b/src/main/java/org/galemc/gale/executor/annotation/thread/ThisThreadOnly.java -new file mode 100644 -index 0000000000000000000000000000000000000000..2544ec2ef1d401696d61c6c75b72f79ffbb21e87 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/annotation/thread/ThisThreadOnly.java -@@ -0,0 +1,32 @@ -+// Gale - thread-safety annotations -+ -+package org.galemc.gale.executor.annotation.thread; -+ -+import org.galemc.gale.executor.annotation.Access; -+ -+import java.lang.annotation.Documented; -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Target; -+ -+/** -+ * An annotation primarily for methods, identifying methods that can only be called from the -+ * enclosing {@link Thread} instance. -+ *
-+ * This annotation can also be used on fields, similar to {@link ThreadRestricted}. -+ *
-+ * This annotation can only be used within {@link Thread} or subclasses thereof. -+ * -+ * @see ThreadRestricted -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@Documented -+@Target({ElementType.METHOD, ElementType.FIELD}) -+public @interface ThisThreadOnly { -+ -+ /** -+ * @see ThreadRestricted#fieldAccess() -+ */ -+ Access value() default Access.READ_WRITE; -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/annotation/thread/ThreadRestricted.java b/src/main/java/org/galemc/gale/executor/annotation/thread/ThreadRestricted.java -new file mode 100644 -index 0000000000000000000000000000000000000000..4a9df03bfa4d683ad32496a20b19ab53015e8d8f ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/annotation/thread/ThreadRestricted.java -@@ -0,0 +1,51 @@ -+// Gale - thread-safety annotations -+ -+package org.galemc.gale.executor.annotation.thread; -+ -+import org.galemc.gale.executor.annotation.Access; -+import org.galemc.gale.executor.annotation.PotentiallyBlocking; -+import org.galemc.gale.executor.annotation.PotentiallyYielding; -+ -+import java.lang.annotation.Documented; -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Target; -+ -+/** -+ * An annotation primarily for methods, identifying methods that can only be called on a specific thread or -+ * set of threads, as described by the given description. -+ *
-+ * When applied to a field, this annotation indicates that the field can only be accessed and/or modified from the -+ * described specific thread or set of threads. -+ *
-+ * When applied to a class, this annotation indicates it holds for all methods and fields, both instance and static, -+ * belonging to the class, or any superclass thereof, or any inner or statically nested class of the class or -+ * any superclass thereof, except for any cases where a conflicting annotation with a smaller scope is also present. -+ *
-+ * In a method annotated with {@link ThreadRestricted}, the only fields and methods that can be used are those -+ * annotated with {@link ThreadRestricted} that have a similar or stricter condition, -+ * unless extra safety guarantees are checked or explicitly provided. -+ * Use of methods that are potentially yielding (at least those annotated with {@link PotentiallyYielding}, but also -+ * those annotated with {@link PotentiallyBlocking} that potentially yield), when the restriction allows for threads -+ * that are not yielding, is discouraged, because some threads cannot yield, and must block instead, -+ * leading to context switches where none may be expected. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@Documented -+@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD}) -+public @interface ThreadRestricted { -+ -+ /** -+ * @return A description of the thread or set of threads that this annotation describes a method or field -+ * being restricted to. -+ */ -+ String value(); -+ -+ /** -+ * @return What type of access this annotation describes, when it is applied to a field. -+ *
-+ * This annotation describes all types of access ({@link Access#READ_WRITE}) by default. -+ */ -+ Access fieldAccess() default Access.READ_WRITE; -+ -+} diff --git a/patches/server/0144-CPU-cores-estimation.patch b/patches/server/0144-CPU-cores-estimation.patch deleted file mode 100644 index 41849df..0000000 --- a/patches/server/0144-CPU-cores-estimation.patch +++ /dev/null @@ -1,189 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Tue, 29 Nov 2022 12:27:47 +0100 -Subject: [PATCH] CPU cores estimation - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java -index 7475f41b3895aa730cae285dec60b7a76006753e..e7d93cb9b219542736ef870bbbefacc359c0b46f 100644 ---- a/src/main/java/co/aikar/timings/TimingsExport.java -+++ b/src/main/java/co/aikar/timings/TimingsExport.java -@@ -37,6 +37,7 @@ import org.bukkit.configuration.MemorySection; - import org.bukkit.entity.EntityType; - import org.galemc.gale.configuration.GaleGlobalConfiguration; - import org.galemc.gale.configuration.timingsexport.VanillaServerPropertiesTimingsExport; -+import org.galemc.gale.util.CPUCoresEstimation; - import org.json.simple.JSONObject; - import org.json.simple.JSONValue; - import oshi.SystemInfo; -@@ -207,6 +208,10 @@ public class TimingsExport extends Thread { - pair("finalizing", ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount()) - )), - pair("cpu", runtime.availableProcessors()), -+ // Gale start - CPU cores estimation - include in timings -+ pair("cpucoresestimation", CPUCoresEstimation.get()), -+ pair("cpuphysicalprocessorcount", processor.getPhysicalProcessorCount()), -+ // Gale end - CPU cores estimation - include in timings - pair("cpuname", hardwareInfo.getProcessor().getProcessorIdentifier().getName().trim()), - pair("hardwarespecs", hardwareSpecsMap), // Gale - include hardware specs in timings - pair("runtime", runtimeBean.getUptime()), -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index 5a92a8f24d3a6957cc3f8404bf3458f4c4058fb4..666114daf9e9a3f9f9e0779a3a40dfac09c80d60 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -49,6 +49,7 @@ import net.minecraft.world.level.block.entity.SkullBlockEntity; - import net.minecraft.world.level.storage.LevelStorageSource; - import org.galemc.gale.command.GaleCommands; - import org.galemc.gale.configuration.GaleGlobalConfiguration; -+import org.galemc.gale.util.CPUCoresEstimation; - import org.slf4j.Logger; - - // CraftBukkit start -@@ -226,6 +227,12 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider - // Paper end - -+ // Gale start - CPU core estimation -+ if (GaleGlobalConfiguration.get().logToConsole.cpuCoresEstimation) { -+ CPUCoresEstimation.log(LOGGER); -+ } -+ // Gale end - CPU core estimation -+ - // Gale start - Pufferfish - SIMD support - // Attempt to detect vectorization - try { -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 59c1496617572544afa241ea0963c1de021a256c..d175d019c865bf9ecc5641d39fb1a29aebde662c 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -255,6 +255,8 @@ import org.bukkit.structure.StructureManager; - import org.bukkit.util.StringUtil; - import org.bukkit.util.permissions.DefaultPermissions; - import org.yaml.snakeyaml.LoaderOptions; -+import org.galemc.gale.configuration.GaleGlobalConfiguration; -+import org.galemc.gale.util.CPUCoresEstimation; - import org.yaml.snakeyaml.Yaml; - import org.yaml.snakeyaml.constructor.SafeConstructor; - import org.yaml.snakeyaml.error.MarkedYAMLException; -diff --git a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java -index 48f6e114a3ead68d72f27f9d5572eacbc7613ac3..1eab552f981e65c29add81718a2ed47e68f9c07d 100644 ---- a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java -+++ b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java -@@ -185,6 +185,7 @@ public class GaleGlobalConfiguration extends ConfigurationPart { - public boolean unrecognizedRecipes = false; // Gale - Purpur - do not log unrecognized recipes - public boolean legacyMaterialInitialization = false; // Gale - Purpur - do not log legacy Material initialization - public boolean playerLoginLocations = true; // Gale - JettPack - make logging login location configurable -+ public boolean cpuCoresEstimation = false; // Gale - CPU cores estimation - - public Chat chat; - public class Chat extends ConfigurationPart { -diff --git a/src/main/java/org/galemc/gale/util/CPUCoresEstimation.java b/src/main/java/org/galemc/gale/util/CPUCoresEstimation.java -new file mode 100644 -index 0000000000000000000000000000000000000000..07020fd1255fade812f4a9af1c76bdd5b7909436 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/util/CPUCoresEstimation.java -@@ -0,0 +1,102 @@ -+// Gale - CPU cores estimation -+ -+package org.galemc.gale.util; -+ -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.slf4j.Logger; -+import oshi.SystemInfo; -+import oshi.hardware.CentralProcessor; -+import oshi.hardware.HardwareAbstractionLayer; -+ -+/** -+ * A utility class to estimate the number of physical CPU cores. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public final class CPUCoresEstimation { -+ -+ private CPUCoresEstimation() {} -+ -+ public static final String environmentVariableKey = "gale.cores"; -+ -+ /** -+ * @return The value of the {@link #environmentVariableKey} environment variable, -+ * or -1 if not set, or -1 if the environment variable is set to a nonpositive value. -+ */ -+ public static int getByEnvironmentVariable() { -+ int value = Integer.getInteger(environmentVariableKey, -1); -+ return value > 0 ? value : -1; -+ } -+ -+ /** -+ * @return The number of cores estimated by OSHI using {@link CentralProcessor#getPhysicalProcessorCount()}, -+ * or -1 if OSHI throws an exception or returns a nonpositive value. -+ */ -+ public static int getByOSHI() { -+ try { -+ SystemInfo systemInfo = new SystemInfo(); -+ HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware(); -+ CentralProcessor centralProcessor = hardwareAbstractionLayer.getProcessor(); -+ int coresAccordingToOSHI = centralProcessor.getPhysicalProcessorCount(); -+ if (coresAccordingToOSHI > 0) { -+ return coresAccordingToOSHI; -+ } -+ } catch (Throwable ignored) {} -+ return -1; -+ } -+ -+ /** -+ * @return A naive estimation of the number of cores, by taking {@link Runtime#availableProcessors()} and applying -+ * floored division by 2, since most systems will have hyper-threading with 2 threads per core. If the result is -+ * nonpositive, this method returns 1. -+ */ -+ public static int getByRuntimeProcessors() { -+ return Math.min(1, Runtime.getRuntime().availableProcessors() / 2); -+ } -+ -+ public static int get() { -+ // Use the environment variable if set -+ int environmentCores = getByEnvironmentVariable(); -+ if (environmentCores > 0) { -+ return environmentCores; -+ } -+ // Use the OSHI library to find the physical processor count -+ int oshiCores = getByOSHI(); -+ if (oshiCores > 0) { -+ return oshiCores; -+ } -+ // Make a guess that the number of CPU cores is half the number of available runtime processor -+ // (i.e. guess that the CPU uses hyper-threading with 2 threads per core) -+ return getByRuntimeProcessors(); -+ } -+ -+ /** -+ * Logs the CPU core estimation to the given {@link Logger}. -+ */ -+ public static void log(Logger logger) { -+ String explanation; -+ int assumedValue; -+ // Use the environment variable if set -+ int environmentCores = getByEnvironmentVariable(); -+ // Use the OSHI library to find the physical processor count -+ int oshiCores = getByOSHI(); -+ if (environmentCores >= 1) { -+ assumedValue = environmentCores; -+ explanation = "(based on the '" + environmentVariableKey + "' environment variable)" + (oshiCores >= 1 ? " (OSHI detected " + oshiCores + " cores)" : ""); -+ } else { -+ explanation = "(because "; -+ if (oshiCores >= 1) { -+ assumedValue = oshiCores; -+ explanation += "OSHI detected " + oshiCores + " physical cores)"; -+ } else { -+ assumedValue = getByRuntimeProcessors(); -+ explanation += "the number of runtime processors is " + Runtime.getRuntime().availableProcessors() + ") (please set the '" + environmentVariableKey + "' environment variable if the number of physical CPU cores is incorrect)"; -+ } -+ } -+ logger.info("The server will assume you have " + assumedValue + " physical CPU cores " + explanation + ""); -+ } -+ -+} diff --git a/patches/server/0145-Virtual-threads-utility.patch b/patches/server/0145-Virtual-threads-utility.patch deleted file mode 100644 index 6955de9..0000000 --- a/patches/server/0145-Virtual-threads-utility.patch +++ /dev/null @@ -1,158 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Mon, 13 Feb 2023 21:40:20 +0100 -Subject: [PATCH] Virtual threads utility - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/org/galemc/gale/util/VirtualThreads.java b/src/main/java/org/galemc/gale/util/VirtualThreads.java -new file mode 100644 -index 0000000000000000000000000000000000000000..0ae0cde2ed65939f84d0f843e2c2b89beb76ed3f ---- /dev/null -+++ b/src/main/java/org/galemc/gale/util/VirtualThreads.java -@@ -0,0 +1,144 @@ -+// Gale - virtual threads utility -+ -+package org.galemc.gale.util; -+ -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.jetbrains.annotations.Nullable; -+ -+import java.lang.reflect.Method; -+import java.util.concurrent.ThreadFactory; -+ -+/** -+ * A utility class to detect and provide the ability to use virtual threads. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@SuppressWarnings({"JavadocReference", "Since15"}) -+@AnyThreadSafe -+@YieldFree -+public final class VirtualThreads { -+ -+ private VirtualThreads() {} -+ -+ /** -+ * The minimum major version of Java that is known to support using virtual threads. -+ */ -+ public static final int minimumJavaMajorVersion = 19; -+ -+ /** -+ * The {@link Thread#ofVirtual()} method, obtained via Reflection, -+ * or null if it could not be obtained. -+ */ -+ public static final @Nullable Method Thread_ofVirtual_method; -+ -+ /** -+ * The {@link Thread.Builder} class, obtained via Reflection, -+ * or null if it could not be obtained. -+ */ -+ public static final @Nullable Class Thread_Builder_class; -+ -+ /** -+ * The {@link Thread.Builder#factory()} method, obtained via Reflection, -+ * or null if it could not be obtained. -+ */ -+ public static final @Nullable Method Thread_Builder_factory_method; -+ -+ /** -+ * The {@link Thread.Builder#start(Runnable)} method, obtained via Reflection, -+ * or null if it could not be obtained. -+ */ -+ public static final @Nullable Method Thread_Builder_start_method; -+ -+ /** -+ * Whether virtual threads are enabled. -+ */ -+ public static final boolean areVirtualThreadsEnabled; -+ -+ /** -+ * @return A {@link ThreadFactory} that produces {@link java.lang.VirtualThread}s, -+ * or null if {@link #areVirtualThreadsEnabled} is false. -+ */ -+ public static @Nullable ThreadFactory createVirtualThreadFactory() { -+ if (!areVirtualThreadsEnabled) { -+ return null; -+ } -+ try { -+ //noinspection DataFlowIssue -+ return (ThreadFactory) Thread_Builder_factory_method.invoke(Thread_ofVirtual_method.invoke(null)); -+ } catch (Exception e) { -+ // We assume this does not happen when areVirtualThreadsEnabled has been set to true -+ throw new RuntimeException(e); -+ } -+ } -+ -+ /** -+ * @param task The runnable for the thread to execute. -+ * @return A {@link java.lang.VirtualThread} that has been started with the given task, -+ * or null if {@link #areVirtualThreadsEnabled} is false. -+ */ -+ public static @Nullable Thread startVirtualThread(Runnable task) { -+ if (!areVirtualThreadsEnabled) { -+ return null; -+ } -+ try { -+ //noinspection DataFlowIssue -+ return (Thread) Thread_Builder_start_method.invoke(Thread_ofVirtual_method.invoke(null), task); -+ } catch (Exception e) { -+ // We assume this does not happen when areVirtualThreadsEnabled has been set to true -+ throw new RuntimeException(e); -+ } -+ } -+ -+ static { -+ { -+ Method method; -+ try { -+ method = Thread.class.getMethod("ofVirtual"); -+ } catch (Throwable ignored) { -+ method = null; -+ } -+ Thread_ofVirtual_method = method; -+ if (Thread_ofVirtual_method != null) { -+ Thread_ofVirtual_method.setAccessible(true); -+ } -+ } -+ { -+ Class clazz; -+ try { -+ //noinspection DataFlowIssue -+ clazz = Thread_ofVirtual_method.invoke(null).getClass(); -+ } catch (Throwable ignored) { -+ clazz = null; -+ } -+ Thread_Builder_class = clazz; -+ } -+ { -+ Method method; -+ try { -+ //noinspection DataFlowIssue -+ method = Thread_Builder_class.getMethod("factory"); -+ } catch (Throwable ignored) { -+ method = null; -+ } -+ Thread_Builder_factory_method = method; -+ if (Thread_Builder_factory_method != null) { -+ Thread_Builder_factory_method.setAccessible(true); -+ } -+ } -+ { -+ Method method; -+ try { -+ method = Thread_Builder_class.getMethod("start"); -+ } catch (Throwable ignored) { -+ method = null; -+ } -+ Thread_Builder_start_method = method; -+ if (Thread_Builder_start_method != null) { -+ Thread_Builder_start_method.setAccessible(true); -+ } -+ } -+ areVirtualThreadsEnabled = Thread_ofVirtual_method != null && Thread_Builder_class != null && Thread_Builder_factory_method != null && Thread_Builder_start_method != null; -+ } -+ -+} diff --git a/patches/server/0146-CheckableLock-utility.patch b/patches/server/0146-CheckableLock-utility.patch deleted file mode 100644 index 7cc18ec..0000000 --- a/patches/server/0146-CheckableLock-utility.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Sun, 5 Feb 2023 19:11:39 +0100 -Subject: [PATCH] CheckableLock utility - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/org/galemc/gale/concurrent/CheckableLock.java b/src/main/java/org/galemc/gale/concurrent/CheckableLock.java -new file mode 100644 -index 0000000000000000000000000000000000000000..749d8d8e2038554a21c2d879349d5189d86a8481 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/concurrent/CheckableLock.java -@@ -0,0 +1,28 @@ -+// Gale - CheckableLock utility -+ -+package org.galemc.gale.concurrent; -+ -+import org.galemc.gale.executor.annotation.YieldFree; -+ -+import java.util.concurrent.locks.Lock; -+ -+/** -+ * A {@link Lock} that also provides an {@link #isLocked()} method. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public interface CheckableLock extends Lock { -+ -+ /** -+ * @return Whether this lock is currently held. -+ */ -+ @YieldFree -+ boolean isLocked(); -+ -+ /** -+ * @return Whether this lock is currently held by the {@link Thread#currentThread}. -+ */ -+ @YieldFree -+ boolean isHeldByCurrentThread(); -+ -+} diff --git a/patches/server/0147-Mutex-utility.patch b/patches/server/0147-Mutex-utility.patch deleted file mode 100644 index 1c4c0d9..0000000 --- a/patches/server/0147-Mutex-utility.patch +++ /dev/null @@ -1,186 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Fri, 2 Dec 2022 10:31:49 +0100 -Subject: [PATCH] Mutex utility - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/org/galemc/gale/concurrent/Mutex.java b/src/main/java/org/galemc/gale/concurrent/Mutex.java -new file mode 100644 -index 0000000000000000000000000000000000000000..649d2cfc1d73699302b4e5e64e9110e7681ae09c ---- /dev/null -+++ b/src/main/java/org/galemc/gale/concurrent/Mutex.java -@@ -0,0 +1,127 @@ -+// Gale - mutex utility -+ -+package org.galemc.gale.concurrent; -+ -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.concurrent.TimeUnit; -+import java.util.concurrent.locks.Lock; -+ -+/** -+ * A mutex, intended to be a more performant alternative to {@link java.util.concurrent.locks.ReentrantLock} -+ * when the reentrant property is not needed. -+ *
-+ * This interface extends {@link Lock}, of which the {@link Lock#lock}, {@link Lock#lockInterruptibly}, -+ * {@link Lock#tryLock} and {@link Lock#unlock} methods are simply deferred to their usual mutex versions, -+ * respectively {@link #acquireUninterruptibly}, {@link #acquire}, {@link #tryAcquire} and -+ * {@link #release}. The {@link Lock#newCondition} method does not have a default implementation. -+ *
-+ * This interface extends {@link AutoCloseable}, where {@link #close()} calls {@link #release()}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+public interface Mutex extends CheckableLock, AutoCloseable { -+ -+ void acquireUninterruptibly(); -+ -+ void acquire() throws InterruptedException; -+ -+ void release(); -+ -+ boolean tryAcquire(); -+ -+ boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException; -+ -+ @Override -+ default void lock() { -+ this.acquireUninterruptibly(); -+ } -+ -+ @Override -+ default void lockInterruptibly() throws InterruptedException { -+ this.acquire(); -+ } -+ -+ @Override -+ default boolean tryLock() { -+ return this.tryAcquire(); -+ } -+ -+ @Override -+ default boolean tryLock(long l, @NotNull TimeUnit timeUnit) throws InterruptedException { -+ return this.tryAcquire(l, timeUnit); -+ } -+ -+ @Override -+ default void unlock() { -+ this.release(); -+ } -+ -+ /** -+ * Acquires this mutex in the style of a spin lock: repeatedly calls {@link #tryAcquire()} until successful. -+ */ -+ default void spinLock() { -+ while (!this.tryAcquire()) { -+ Thread.onSpinWait(); -+ } -+ } -+ -+ /** -+ * Acquires this mutex with {@link #acquire()}, then returns this instance. -+ *
-+ * This can be used in the following way: -+ *

-+ * -+ *  try (mutex.withLock()) {
-+ *   // code
-+ *  } -+ *
-+ *

-+ * -+ * @return This {@link Mutex}, which implements {@link AutoCloseable}. -+ */ -+ default Mutex withLock() throws InterruptedException { -+ this.acquire(); -+ return this; -+ } -+ -+ /** -+ * Acquires this mutex with {@link #spinLock()}, then returns this instance. -+ * -+ * @return This {@link Mutex}, which implements {@link AutoCloseable}. -+ * @see #withLock() -+ */ -+ default Mutex withSpinLock() { -+ this.spinLock(); -+ return this; -+ } -+ -+ @Override -+ default void close() { -+ this.release(); -+ } -+ -+ /** -+ * Instantiates a new {@link Mutex}, with the default implementation -+ * that should be geared towards performance. -+ */ -+ static @NotNull Mutex create() { -+ return new SemaphoreMutex(); -+ } -+ -+ /** -+ * Instantiates a new {@link Mutex} that we intend to use as a {@link Lock}, with a default implementation -+ * that should be geared towards performance of the {@link Lock} methods. -+ */ -+ static @NotNull Mutex createLock() { -+ return create(); -+ } -+ -+ @Override -+ default boolean isHeldByCurrentThread() { -+ throw new UnsupportedOperationException("isHeldByCurrentThread() is not supported for Mutex, and is not implemented by the implementation " + this.getClass().getName() + " either"); -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d31293a2a2151bc9fbdc6eb2175045b429fb4461 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java -@@ -0,0 +1,39 @@ -+// Gale - mutex utility -+ -+package org.galemc.gale.concurrent; -+ -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.concurrent.Semaphore; -+import java.util.concurrent.locks.Condition; -+import java.util.concurrent.locks.Lock; -+ -+/** -+ * A {@link Mutex}, with implements the required methods by extending {@link Semaphore}, -+ * and throws {@link UnsupportedOperationException} for all {@link Lock} methods that do not have a default -+ * implementation in {@link Mutex}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public class SemaphoreMutex extends Semaphore implements Mutex { -+ -+ public SemaphoreMutex() { -+ super(1); -+ } -+ -+ @NotNull -+ @Override -+ public Condition newCondition() { -+ throw new UnsupportedOperationException("newCondition() is not implemented for SemaphoreMutex"); -+ } -+ -+ @Override -+ public boolean isLocked() { -+ return this.availablePermits() == 0; -+ } -+ -+} diff --git a/patches/server/0148-Thread-aware-lock-utility.patch b/patches/server/0148-Thread-aware-lock-utility.patch deleted file mode 100644 index a8653bf..0000000 --- a/patches/server/0148-Thread-aware-lock-utility.patch +++ /dev/null @@ -1,127 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Fri, 3 Feb 2023 23:01:51 +0100 -Subject: [PATCH] Thread-aware lock utility - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/org/galemc/gale/concurrent/ThreadAwareNonReentrantLock.java b/src/main/java/org/galemc/gale/concurrent/ThreadAwareNonReentrantLock.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8c14d90d1a907adf994070cbe5d62f1fbfd8d9c8 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/concurrent/ThreadAwareNonReentrantLock.java -@@ -0,0 +1,113 @@ -+// Gale - thread-aware lock utility -+ -+package org.galemc.gale.concurrent; -+ -+import net.minecraft.server.MinecraftServer; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.concurrent.TimeUnit; -+import java.util.concurrent.locks.Condition; -+import java.util.concurrent.locks.Lock; -+ -+/** -+ * A wrapper for a non-reentrant {@link Lock}, that is aware when the thread that already holds this lock -+ * is trying to acquire it again (for example by calling {@link #tryLock}), and throws an exception in that case. -+ *
-+ * This is useful for debugging purposes when a {@link Lock} is not supposed to be reachable -+ * from any code that is executed while the lock is being held. -+ */ -+public class ThreadAwareNonReentrantLock implements CheckableLock { -+ -+ private final CheckableLock innerLock; -+ -+ /** -+ * The {@link Thread} that currently holds this lock, or null if no thread currently holds this lock. -+ */ -+ private volatile @Nullable Thread currentHoldingThread; -+ -+ public ThreadAwareNonReentrantLock(CheckableLock innerLock) { -+ this.innerLock = innerLock; -+ } -+ -+ @Override -+ public void lock() { -+ var currentThread = Thread.currentThread(); -+ if (this.currentHoldingThread == currentThread) { -+ IllegalStateException exception = new IllegalStateException("Called lock() on a " + this.getClass().getSimpleName() + " from the thread (" + currentThread + ") that was already holding it"); -+ MinecraftServer.LOGGER.error(exception.getMessage() + ", at:"); -+ exception.printStackTrace(); -+ throw exception; -+ } -+ this.innerLock.lock(); -+ this.currentHoldingThread = currentThread; -+ } -+ -+ @Override -+ public void lockInterruptibly() throws InterruptedException { -+ var currentThread = Thread.currentThread(); -+ if (this.currentHoldingThread == currentThread) { -+ IllegalStateException exception = new IllegalStateException("Called lockInterruptibly() on a " + this.getClass().getSimpleName() + " from the thread (" + currentThread + ") that was already holding it"); -+ MinecraftServer.LOGGER.error(exception.getMessage() + ", at:"); -+ exception.printStackTrace(); -+ throw exception; -+ } -+ this.innerLock.lockInterruptibly(); -+ this.currentHoldingThread = currentThread; -+ } -+ -+ @Override -+ public boolean tryLock() { -+ var currentThread = Thread.currentThread(); -+ if (this.currentHoldingThread == currentThread) { -+ IllegalStateException exception = new IllegalStateException("Called tryLock() on a " + this.getClass().getSimpleName() + " from the thread (" + currentThread + ") that was already holding it"); -+ MinecraftServer.LOGGER.error(exception.getMessage() + ", at:"); -+ exception.printStackTrace(); -+ throw exception; -+ } -+ if (this.innerLock.tryLock()) { -+ this.currentHoldingThread = currentThread; -+ return true; -+ } -+ return false; -+ } -+ -+ @Override -+ public boolean tryLock(long time, @NotNull TimeUnit unit) throws InterruptedException { -+ var currentThread = Thread.currentThread(); -+ if (this.currentHoldingThread == currentThread) { -+ IllegalStateException exception = new IllegalStateException("Called tryLock() on a " + this.getClass().getSimpleName() + " from the thread (" + currentThread + ") that was already holding it"); -+ MinecraftServer.LOGGER.error(exception.getMessage() + ", at:"); -+ exception.printStackTrace(); -+ throw exception; -+ } -+ if (this.innerLock.tryLock(time, unit)) { -+ this.currentHoldingThread = currentThread; -+ return true; -+ } -+ return false; -+ } -+ -+ @Override -+ public void unlock() { -+ this.innerLock.unlock(); -+ this.currentHoldingThread = null; -+ } -+ -+ @NotNull -+ @Override -+ public Condition newCondition() { -+ return this.innerLock.newCondition(); -+ } -+ -+ @Override -+ public boolean isLocked() { -+ return this.innerLock.isLocked(); -+ } -+ -+ @Override -+ public boolean isHeldByCurrentThread() { -+ return this.innerLock.isHeldByCurrentThread(); -+ } -+ -+} diff --git a/patches/server/0149-Unterminable-executor-utility.patch b/patches/server/0149-Unterminable-executor-utility.patch deleted file mode 100644 index 7eeb205..0000000 --- a/patches/server/0149-Unterminable-executor-utility.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Mon, 5 Dec 2022 11:21:05 +0100 -Subject: [PATCH] Unterminable executor utility - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/org/galemc/gale/concurrent/UnterminableExecutorService.java b/src/main/java/org/galemc/gale/concurrent/UnterminableExecutorService.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ce53ca5aa53cc1c11b36afba17615a91c18834d3 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/concurrent/UnterminableExecutorService.java -@@ -0,0 +1,50 @@ -+// Gale - unterminable executor utility -+ -+package org.galemc.gale.concurrent; -+ -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.List; -+import java.util.concurrent.AbstractExecutorService; -+import java.util.concurrent.ExecutorService; -+import java.util.concurrent.TimeUnit; -+ -+/** -+ * An {@link ExecutorService} that directly extends {@link AbstractExecutorService}, and cannot be shut down. -+ * This type of executor is useful for an executor that merely exists to implement the {@link ExecutorService} -+ * interface and forward tasks to another executor or executor's queue, and therefore creates no threads that need -+ * to stop either. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+public abstract class UnterminableExecutorService extends AbstractExecutorService { -+ -+ @Override -+ public void shutdown() { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @NotNull -+ @Override -+ public List shutdownNow() { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public boolean isShutdown() { -+ return false; -+ } -+ -+ @Override -+ public boolean isTerminated() { -+ return false; -+ } -+ -+ @Override -+ public boolean awaitTermination(long l, @NotNull TimeUnit timeUnit) throws InterruptedException { -+ throw new UnsupportedOperationException(); -+ } -+ -+} diff --git a/patches/server/0150-FIFO-concurrent-queue-utility.patch b/patches/server/0150-FIFO-concurrent-queue-utility.patch deleted file mode 100644 index 68cf393..0000000 --- a/patches/server/0150-FIFO-concurrent-queue-utility.patch +++ /dev/null @@ -1,148 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Sun, 11 Dec 2022 16:24:15 +0100 -Subject: [PATCH] FIFO concurrent queue utility - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/org/galemc/gale/collection/FIFOConcurrentLinkedQueue.java b/src/main/java/org/galemc/gale/collection/FIFOConcurrentLinkedQueue.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f59e71b5942e8e7fb142e605dd4986a3c6752ed4 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/collection/FIFOConcurrentLinkedQueue.java -@@ -0,0 +1,134 @@ -+// Gale - FIFO concurrent queue utility -+ -+package org.galemc.gale.collection; -+ -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.Collection; -+import java.util.Iterator; -+import java.util.Queue; -+import java.util.concurrent.ConcurrentLinkedDeque; -+import java.util.concurrent.ConcurrentLinkedQueue; -+ -+/** -+ * A utility class that implements the {@link Queue} interface, and provides an identical implementation to -+ * {@link ConcurrentLinkedQueue}, except for adding elements, which appends the given element to the front (head) -+ * instead of the back (tail) of this queue. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public class FIFOConcurrentLinkedQueue implements Queue { -+ -+ private final ConcurrentLinkedDeque deque; -+ -+ public FIFOConcurrentLinkedQueue() { -+ this.deque = new ConcurrentLinkedDeque<>(); -+ } -+ -+ public FIFOConcurrentLinkedQueue(Collection c) { -+ this.deque = new ConcurrentLinkedDeque<>(c); -+ } -+ -+ @Override -+ public int size() { -+ return this.deque.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return this.deque.isEmpty(); -+ } -+ -+ @Override -+ public boolean contains(Object o) { -+ return this.deque.contains(o); -+ } -+ -+ @NotNull -+ @Override -+ public Iterator iterator() { -+ return this.deque.iterator(); -+ } -+ -+ @NotNull -+ @Override -+ public Object[] toArray() { -+ return this.deque.toArray(); -+ } -+ -+ @NotNull -+ @Override -+ public T[] toArray(@NotNull T[] ts) { -+ return this.deque.toArray(ts); -+ } -+ -+ @Override -+ public boolean add(E e) { -+ this.deque.addFirst(e); -+ return true; -+ } -+ -+ @Override -+ public boolean remove(Object o) { -+ return this.deque.remove(o); -+ } -+ -+ @Override -+ public boolean containsAll(@NotNull Collection collection) { -+ return this.deque.containsAll(collection); -+ } -+ -+ @Override -+ public boolean addAll(@NotNull Collection collection) { -+ boolean changed = false; -+ for (E element : collection) { -+ changed |= this.add(element); -+ } -+ return changed; -+ } -+ -+ @Override -+ public boolean removeAll(@NotNull Collection collection) { -+ return this.deque.removeAll(collection); -+ } -+ -+ @Override -+ public boolean retainAll(@NotNull Collection collection) { -+ return this.deque.retainAll(collection); -+ } -+ -+ @Override -+ public void clear() { -+ this.deque.clear(); -+ } -+ -+ @Override -+ public boolean offer(E e) { -+ return this.deque.offer(e); -+ } -+ -+ @Override -+ public E remove() { -+ return this.deque.remove(); -+ } -+ -+ @Override -+ public E poll() { -+ return this.deque.poll(); -+ } -+ -+ @Override -+ public E element() { -+ return this.deque.element(); -+ } -+ -+ @Override -+ public E peek() { -+ return this.deque.peek(); -+ } -+ -+} diff --git a/patches/server/0151-Base-thread-pool.patch b/patches/server/0151-Base-thread-pool.patch deleted file mode 100644 index bef3253..0000000 --- a/patches/server/0151-Base-thread-pool.patch +++ /dev/null @@ -1,6644 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Sun, 29 Jan 2023 23:41:12 +0100 -Subject: [PATCH] Base thread pool - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -index cab91880a08c6fdc545804911d295e0f24f4d983..f8bf00ac23f8b86a5dab81668d595d4ad043f09f 100644 ---- a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -22,6 +22,7 @@ import net.minecraft.world.level.block.EntityBlock; - import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.level.chunk.*; - import org.bukkit.Bukkit; -+import org.galemc.gale.executor.queue.ScheduledServerThreadTaskQueues; - - import java.util.*; - import java.util.concurrent.Executor; -@@ -181,7 +182,7 @@ public final class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockCo - - if (!Bukkit.isPrimaryThread()) { - // Plugins? -- MinecraftServer.getServer().scheduleOnMain(() -> modifyBlocks(chunkPacket, chunkPacketInfo)); -+ ScheduledServerThreadTaskQueues.add(() -> modifyBlocks(chunkPacket, chunkPacketInfo), ScheduledServerThreadTaskQueues.ANTI_XRAY_MODIFY_BLOCKS_TASK_MAX_DELAY); // Gale - base thread pool - return; - } - -diff --git a/src/main/java/com/mojang/logging/LogUtils.java b/src/main/java/com/mojang/logging/LogUtils.java -index 49019b4a9bc4e634d54a9b0acaf9229a5c896f85..6aae3b36bfe3ffc630cd7af250633de3444095e8 100644 ---- a/src/main/java/com/mojang/logging/LogUtils.java -+++ b/src/main/java/com/mojang/logging/LogUtils.java -@@ -6,6 +6,7 @@ import org.apache.logging.log4j.core.LifeCycle; - import org.apache.logging.log4j.core.config.Configuration; - import org.apache.logging.log4j.core.config.LoggerConfig; - import org.apache.logging.log4j.spi.LoggerContext; -+import org.jetbrains.annotations.NotNull; - import org.slf4j.Logger; - import org.slf4j.LoggerFactory; - import org.slf4j.Marker; -@@ -66,4 +67,329 @@ public class LogUtils { - return LoggerFactory.getLogger(STACK_WALKER.getCallerClass().getSimpleName()); - } - // Paper end -+ -+ // Gale start - base thread pool - thread loggers -+ public static @NotNull Logger prefixLogger(@NotNull Logger logger, @NotNull Supplier<@NotNull String> prefixSupplier) { -+ return new org.slf4j.Logger() { -+ @Override -+ public String getName() { -+ return logger.getName(); -+ } -+ -+ @Override -+ public boolean isTraceEnabled() { -+ return logger.isTraceEnabled(); -+ } -+ -+ @Override -+ public void trace(String msg) { -+ logger.trace(prefixSupplier.get() + msg); -+ } -+ -+ @Override -+ public void trace(String format, Object arg) { -+ logger.trace(prefixSupplier.get() + format, arg); -+ } -+ -+ @Override -+ public void trace(String format, Object arg1, Object arg2) { -+ logger.trace(prefixSupplier.get() + format, arg1, arg2); -+ } -+ -+ @Override -+ public void trace(String format, Object... arguments) { -+ logger.trace(prefixSupplier.get() + format, arguments); -+ } -+ -+ @Override -+ public void trace(String msg, Throwable t) { -+ logger.trace(prefixSupplier.get() + msg, t); -+ } -+ -+ @Override -+ public boolean isTraceEnabled(Marker marker) { -+ return logger.isTraceEnabled(marker); -+ } -+ -+ @Override -+ public void trace(Marker marker, String msg) { -+ logger.trace(marker, prefixSupplier.get() + msg); -+ } -+ -+ @Override -+ public void trace(Marker marker, String format, Object arg) { -+ logger.trace(marker, prefixSupplier.get() + format, arg); -+ } -+ -+ @Override -+ public void trace(Marker marker, String format, Object arg1, Object arg2) { -+ logger.trace(marker, prefixSupplier.get() + format, arg1, arg2); -+ } -+ -+ @Override -+ public void trace(Marker marker, String format, Object... argArray) { -+ logger.trace(marker, prefixSupplier.get() + format, argArray); -+ } -+ -+ @Override -+ public void trace(Marker marker, String msg, Throwable t) { -+ logger.trace(marker, prefixSupplier.get() + msg, t); -+ } -+ -+ @Override -+ public boolean isDebugEnabled() { -+ return logger.isDebugEnabled(); -+ } -+ -+ @Override -+ public void debug(String msg) { -+ logger.debug(prefixSupplier.get() + msg); -+ } -+ -+ @Override -+ public void debug(String format, Object arg) { -+ logger.debug(prefixSupplier.get() + format, arg); -+ } -+ -+ @Override -+ public void debug(String format, Object arg1, Object arg2) { -+ logger.debug(prefixSupplier.get() + format, arg1, arg2); -+ } -+ -+ @Override -+ public void debug(String format, Object... arguments) { -+ logger.debug(prefixSupplier.get() + format, arguments); -+ } -+ -+ @Override -+ public void debug(String msg, Throwable t) { -+ logger.debug(prefixSupplier.get() + msg, t); -+ } -+ -+ @Override -+ public boolean isDebugEnabled(Marker marker) { -+ return logger.isDebugEnabled(marker); -+ } -+ -+ @Override -+ public void debug(Marker marker, String msg) { -+ logger.debug(marker, prefixSupplier.get() + msg); -+ } -+ -+ @Override -+ public void debug(Marker marker, String format, Object arg) { -+ logger.debug(marker, prefixSupplier.get() + format, arg); -+ } -+ -+ @Override -+ public void debug(Marker marker, String format, Object arg1, Object arg2) { -+ logger.debug(marker, prefixSupplier.get() + format, arg1, arg2); -+ } -+ -+ @Override -+ public void debug(Marker marker, String format, Object... arguments) { -+ logger.debug(marker, prefixSupplier.get() + format, arguments); -+ } -+ -+ @Override -+ public void debug(Marker marker, String msg, Throwable t) { -+ logger.debug(marker, prefixSupplier.get() + msg, t); -+ } -+ -+ @Override -+ public boolean isInfoEnabled() { -+ return logger.isInfoEnabled(); -+ } -+ -+ @Override -+ public void info(String msg) { -+ logger.info(prefixSupplier.get() + msg); -+ } -+ -+ @Override -+ public void info(String format, Object arg) { -+ logger.info(prefixSupplier.get() + format, arg); -+ } -+ -+ @Override -+ public void info(String format, Object arg1, Object arg2) { -+ logger.info(prefixSupplier.get() + format, arg1, arg2); -+ } -+ -+ @Override -+ public void info(String format, Object... arguments) { -+ logger.info(prefixSupplier.get() + format, arguments); -+ } -+ -+ @Override -+ public void info(String msg, Throwable t) { -+ logger.info(prefixSupplier.get() + msg, t); -+ } -+ -+ @Override -+ public boolean isInfoEnabled(Marker marker) { -+ return logger.isInfoEnabled(marker); -+ } -+ -+ @Override -+ public void info(Marker marker, String msg) { -+ logger.info(marker, prefixSupplier.get() + msg); -+ } -+ -+ @Override -+ public void info(Marker marker, String format, Object arg) { -+ logger.info(marker, prefixSupplier.get() + format, arg); -+ } -+ -+ @Override -+ public void info(Marker marker, String format, Object arg1, Object arg2) { -+ logger.info(marker, prefixSupplier.get() + format, arg1, arg2); -+ } -+ -+ @Override -+ public void info(Marker marker, String format, Object... arguments) { -+ logger.info(marker, prefixSupplier.get() + format, arguments); -+ } -+ -+ @Override -+ public void info(Marker marker, String msg, Throwable t) { -+ logger.info(marker, prefixSupplier.get() + msg, t); -+ } -+ -+ @Override -+ public boolean isWarnEnabled() { -+ return logger.isWarnEnabled(); -+ } -+ -+ @Override -+ public void warn(String msg) { -+ logger.warn(prefixSupplier.get() + msg); -+ } -+ -+ @Override -+ public void warn(String format, Object arg) { -+ logger.warn(prefixSupplier.get() + format, arg); -+ } -+ -+ @Override -+ public void warn(String format, Object arg1, Object arg2) { -+ logger.warn(prefixSupplier.get() + format, arg1, arg2); -+ } -+ -+ @Override -+ public void warn(String format, Object... arguments) { -+ logger.warn(prefixSupplier.get() + format, arguments); -+ } -+ -+ @Override -+ public void warn(String msg, Throwable t) { -+ logger.warn(prefixSupplier.get() + msg, t); -+ } -+ -+ @Override -+ public boolean isWarnEnabled(Marker marker) { -+ return logger.isWarnEnabled(marker); -+ } -+ -+ @Override -+ public void warn(Marker marker, String msg) { -+ logger.warn(marker, prefixSupplier.get() + msg); -+ } -+ -+ @Override -+ public void warn(Marker marker, String format, Object arg) { -+ logger.warn(marker, prefixSupplier.get() + format, arg); -+ } -+ -+ @Override -+ public void warn(Marker marker, String format, Object arg1, Object arg2) { -+ logger.warn(marker, prefixSupplier.get() + format, arg1, arg2); -+ } -+ -+ @Override -+ public void warn(Marker marker, String format, Object... arguments) { -+ logger.warn(marker, prefixSupplier.get() + format, arguments); -+ } -+ -+ @Override -+ public void warn(Marker marker, String msg, Throwable t) { -+ logger.warn(marker, prefixSupplier.get() + msg, t); -+ } -+ -+ @Override -+ public boolean isErrorEnabled() { -+ return logger.isErrorEnabled(); -+ } -+ -+ @Override -+ public void error(String msg) { -+ logger.error(prefixSupplier.get() + msg); -+ } -+ -+ @Override -+ public void error(String format, Object arg) { -+ logger.error(prefixSupplier.get() + format, arg); -+ } -+ -+ @Override -+ public void error(String format, Object arg1, Object arg2) { -+ logger.error(prefixSupplier.get() + format, arg1, arg2); -+ } -+ -+ @Override -+ public void error(String format, Object... arguments) { -+ logger.error(prefixSupplier.get() + format, arguments); -+ } -+ -+ @Override -+ public void error(String msg, Throwable t) { -+ logger.error(prefixSupplier.get() + msg, t); -+ } -+ -+ @Override -+ public boolean isErrorEnabled(Marker marker) { -+ return logger.isErrorEnabled(marker); -+ } -+ -+ @Override -+ public void error(Marker marker, String msg) { -+ logger.error(marker, prefixSupplier.get() + msg); -+ } -+ -+ @Override -+ public void error(Marker marker, String format, Object arg) { -+ logger.error(marker, prefixSupplier.get() + format, arg); -+ } -+ -+ @Override -+ public void error(Marker marker, String format, Object arg1, Object arg2) { -+ logger.error(marker, prefixSupplier.get() + format, arg1, arg2); -+ } -+ -+ @Override -+ public void error(Marker marker, String format, Object... arguments) { -+ logger.error(marker, prefixSupplier.get() + format, arguments); -+ } -+ -+ @Override -+ public void error(Marker marker, String msg, Throwable t) { -+ logger.error(marker, prefixSupplier.get() + msg, t); -+ } -+ -+ }; -+ } -+ -+ public static @NotNull Logger prefixLoggerWithThread(@NotNull Logger logger) { -+ return prefixLogger(logger, () -> "[" + Thread.currentThread().getName() + "] "); -+ } -+ -+ public static @NotNull Logger getLoggerPrefixedWithThread() { -+ return prefixLoggerWithThread(LoggerFactory.getLogger(STACK_WALKER.getCallerClass())); -+ } -+ -+ public static @NotNull Logger getClassLoggerPrefixedWithThread() { -+ return prefixLoggerWithThread(LoggerFactory.getLogger(STACK_WALKER.getCallerClass().getSimpleName())); -+ } -+ // Gale end - base thread pool - thread loggers -+ - } -diff --git a/src/main/java/io/papermc/paper/configuration/Configurations.java b/src/main/java/io/papermc/paper/configuration/Configurations.java -index 80a8362c299d02c26781f06be5b8ee6cada8efbe..ffd46f3fd23ee4f1ba550b4d9a5e9a6ad2f9590e 100644 ---- a/src/main/java/io/papermc/paper/configuration/Configurations.java -+++ b/src/main/java/io/papermc/paper/configuration/Configurations.java -@@ -322,7 +322,7 @@ public abstract class Configurations { - YamlConfiguration global = YamlConfiguration.loadConfiguration(this.globalFolder.resolve(this.globalConfigFileName).toFile()); - ConfigurationSection worlds = global.createSection(legacyWorldsSectionKey); - worlds.set(legacyWorldDefaultsSectionKey, YamlConfiguration.loadConfiguration(this.globalFolder.resolve(this.defaultWorldConfigFileName).toFile())); -- for (ServerLevel level : server.getAllLevels()) { -+ for (ServerLevel level : server.getAllLevelsArray()) { // Gale - base thread pool - optimize server levels - worlds.set(level.getWorld().getName(), YamlConfiguration.loadConfiguration(getWorldConfigFile(level).toFile())); - } - return global; -diff --git a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java -index 1376290026401424619f95dead7fca63681b3994..760601de028536aec142d195aa875ae59edd8e41 100644 ---- a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java -+++ b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java -@@ -289,7 +289,7 @@ public class PaperConfigurations extends Configurations void ensureRunningOnSameThread(Packet packet, T listener, ServerLevel world) throws RunningOnDifferentThreadException { -- PacketUtils.ensureRunningOnSameThread(packet, listener, (BlockableEventLoop) world.getServer()); -+ PacketUtils.ensureRunningOnSameThread(packet, listener, world.getServer()); // Gale - base thread pool - } - -- public static void ensureRunningOnSameThread(Packet packet, T listener, BlockableEventLoop engine) throws RunningOnDifferentThreadException { -+ public static void ensureRunningOnSameThread(Packet packet, T listener, AbstractBlockableEventLoop engine) throws RunningOnDifferentThreadException { // Gale - base thread pool - if (!engine.isSameThread()) { - engine.execute(() -> { // Paper - Fix preemptive player kick on a server shutdown. - packetProcessing.push(listener); // Paper - detailed watchdog information -diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java -index 95462967b057e00b0616857e3c4eb05e5840ba3d..6e05408e299bd72777142bf3fafb17ecdbd50e17 100644 ---- a/src/main/java/net/minecraft/server/Main.java -+++ b/src/main/java/net/minecraft/server/Main.java -@@ -1,12 +1,10 @@ - package net.minecraft.server; - --import com.mojang.authlib.GameProfile; --import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; - import com.mojang.datafixers.DataFixer; - import com.mojang.datafixers.util.Pair; - import com.mojang.logging.LogUtils; - import com.mojang.serialization.DynamicOps; --import com.mojang.serialization.Lifecycle; -+ - import java.awt.GraphicsEnvironment; - import java.io.File; - import java.io.IOException; -@@ -16,17 +14,15 @@ import java.nio.file.Files; - import java.nio.file.Path; - import java.nio.file.Paths; - import java.util.Optional; --import java.util.UUID; - import java.util.function.BooleanSupplier; -+ -+import com.mojang.serialization.Lifecycle; - import io.papermc.paper.world.ThreadedWorldUpgrader; --import joptsimple.NonOptionArgumentSpec; --import joptsimple.OptionParser; - import joptsimple.OptionSet; - import joptsimple.OptionSpec; - import joptsimple.util.PathConverter; - import joptsimple.util.PathProperties; - import net.minecraft.CrashReport; --import net.minecraft.DefaultUncaughtExceptionHandler; - import net.minecraft.SharedConstants; - import net.minecraft.Util; - import net.minecraft.commands.Commands; -@@ -63,13 +59,15 @@ import net.minecraft.world.level.storage.LevelStorageSource; - import net.minecraft.world.level.storage.LevelSummary; - import net.minecraft.world.level.storage.PrimaryLevelData; - import net.minecraft.world.level.storage.WorldData; -+import org.galemc.gale.executor.queue.BaseTaskQueueTier; -+import org.galemc.gale.util.VirtualThreads; - import org.slf4j.Logger; - - // CraftBukkit start - import com.google.common.base.Charsets; - import java.io.InputStreamReader; - import java.util.concurrent.atomic.AtomicReference; --import net.minecraft.SharedConstants; -+ - import org.bukkit.configuration.file.YamlConfiguration; - // CraftBukkit end - -@@ -182,6 +180,29 @@ public class Main { - } - // Paper end - -+ // Gale start - base thread pool - inform if virtual threads are not available -+ if (!VirtualThreads.areVirtualThreadsEnabled) { -+ int majorVersion; -+ try { -+ String version = System.getProperty("java.version"); -+ majorVersion = Integer.parseInt(version.substring(0, version.indexOf('.'))); -+ } catch (Throwable ignored) { -+ /* -+ If we cannot determine the major version, -+ just print a virtual thread warning instead of a version warning. -+ */ -+ majorVersion = -1; -+ } -+ if (majorVersion == -1 || majorVersion >= VirtualThreads.minimumJavaMajorVersion) { -+ Main.LOGGER.warn("Virtual threads are not enabled"); -+ Main.LOGGER.warn("To enable additional powerful optimizations, add \"--enable-preview --add-opens=java.base/java.lang=ALL-UNNAMED\" to your startup flags, BEFORE the \"-jar\"."); -+ } else { -+ Main.LOGGER.warn("You are using Java " + majorVersion + "."); -+ Main.LOGGER.warn("It is strongly recommended to run Gale with Java " + VirtualThreads.minimumJavaMajorVersion + " or higher to enable additional powerful optimizations."); -+ } -+ } -+ // Gale end - base thread pool - inform if virtual threads are not available -+ - org.spigotmc.SpigotConfig.disabledAdvancements = spigotConfiguration.getStringList("advancements.disabled"); // Paper - fix SPIGOT-5885, must be set early in init - // Paper start - fix SPIGOT-5824 - File file; -@@ -240,6 +261,12 @@ public class Main { - - WorldStem worldstem; - -+ // Gale start - base thread pool -+ // Initialize the task tiers and queues by calling an arbitrary method on the last tier and queue -+ //noinspection ResultOfMethodCallIgnored -+ BaseTaskQueueTier.ASYNC.ordinal(); -+ // Gale end - base thread pool -+ - try { - WorldLoader.InitConfig worldloader_c = Main.loadOrCreateConfig(dedicatedserversettings.getProperties(), convertable_conversionsession, flag, resourcepackrepository); - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 9a7ef74ad82b1de6e386994dea0d99a9240ad7af..fe35347639364caf5edfca799f68fd4b41b9e913 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -41,10 +41,8 @@ import java.util.Optional; - import java.util.Set; - import java.util.concurrent.CompletableFuture; - import java.util.concurrent.Executor; --import java.util.concurrent.RejectedExecutionException; - import java.util.concurrent.atomic.AtomicReference; - import java.util.function.BooleanSupplier; --import java.util.function.Consumer; - import java.util.function.Function; - import java.util.stream.Collectors; - import java.util.stream.Stream; -@@ -109,19 +107,8 @@ import net.minecraft.util.ProgressListener; - import net.minecraft.util.RandomSource; - import net.minecraft.util.SignatureValidator; - import net.minecraft.util.datafix.DataFixers; --import net.minecraft.util.profiling.EmptyProfileResults; --import net.minecraft.util.profiling.ProfileResults; --import net.minecraft.util.profiling.ProfilerFiller; --import net.minecraft.util.profiling.ResultField; --import net.minecraft.util.profiling.SingleTickProfiler; - import net.minecraft.util.profiling.jfr.JvmProfiler; - import net.minecraft.util.profiling.jfr.callback.ProfiledDuration; --import net.minecraft.util.profiling.metrics.profiling.ActiveMetricsRecorder; --import net.minecraft.util.profiling.metrics.profiling.InactiveMetricsRecorder; --import net.minecraft.util.profiling.metrics.profiling.MetricsRecorder; --import net.minecraft.util.profiling.metrics.profiling.ServerMetricsSamplersProvider; --import net.minecraft.util.profiling.metrics.storage.MetricsPersister; --import net.minecraft.util.thread.ReentrantBlockableEventLoop; - import net.minecraft.world.Difficulty; - import net.minecraft.world.entity.Entity; - import net.minecraft.world.entity.ai.village.VillageSiege; -@@ -161,7 +148,14 @@ import net.minecraft.world.level.storage.loot.LootTables; - import net.minecraft.world.level.storage.loot.PredicateManager; - import net.minecraft.world.phys.Vec2; - import net.minecraft.world.phys.Vec3; -+import org.galemc.gale.executor.MinecraftServerBlockableEventLoop; - import org.galemc.gale.configuration.GaleConfigurations; -+import org.galemc.gale.executor.annotation.thread.OriginalServerThreadOnly; -+import org.galemc.gale.executor.queue.ScheduledServerThreadTaskQueues; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.OriginalServerThread; -+import org.galemc.gale.executor.thread.pool.BaseThreadActivation; -+import org.jetbrains.annotations.NotNull; - import org.slf4j.Logger; - - // CraftBukkit start -@@ -181,24 +175,27 @@ import net.minecraft.world.level.levelgen.PatrolSpawner; - import net.minecraft.world.level.levelgen.PhantomSpawner; - import net.minecraft.world.level.levelgen.WorldDimensions; - import net.minecraft.world.level.levelgen.presets.WorldPresets; --import org.bukkit.Bukkit; --import org.bukkit.craftbukkit.CraftServer; --import org.bukkit.craftbukkit.Main; --import org.bukkit.craftbukkit.util.CraftChatMessage; --import org.bukkit.craftbukkit.util.LazyPlayerSet; --import org.bukkit.event.player.AsyncPlayerChatPreviewEvent; - import org.bukkit.event.server.ServerLoadEvent; - // CraftBukkit end - - import co.aikar.timings.MinecraftTimings; // Paper - --public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements CommandSource, AutoCloseable { -+public abstract class MinecraftServer extends MinecraftServerBlockableEventLoop implements CommandSource, AutoCloseable { // Gale - base thread pool - - public static final int SERVER_THREAD_PRIORITY = Integer.getInteger("gale.thread.priority.server", 8); // Gale - server thread priority environment variable - -- private static MinecraftServer SERVER; // Paper -+ // Gale start - base thread pool -+ public static MinecraftServer SERVER; // Paper // Gale - base thread pool - private -> public -+ -+ /** -+ * Whether {@link #SERVER} has been set. -+ */ -+ public static boolean isConstructed; -+ -+ // Gale end - base thread pool - public static final Logger LOGGER = LogUtils.getLogger(); - public static final net.kyori.adventure.text.logger.slf4j.ComponentLogger COMPONENT_LOGGER = net.kyori.adventure.text.logger.slf4j.ComponentLogger.logger(LOGGER.getName()); // Paper -+ public static final Optional THREAD_DEBUG_LOGGER = Boolean.FALSE ? Optional.of(LogUtils.prefixLoggerWithThread(LogUtils.prefixLogger(LogUtils.getLogger(), () -> "TEMP DEBUG - "))) : Optional.empty(); // Gale - base thread pool - temporary debug logger - public static final String VANILLA_BRAND = "vanilla"; - private static final float AVERAGE_TICK_TIME_SMOOTHING = 0.8F; - private static final int TICK_STATS_SPAN = 100; -@@ -230,6 +227,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop registries; - private Map, ServerLevel> levels; -+ // Gale start - base thread pool - optimize server levels -+ private @NotNull ServerLevel @NotNull [] levelArray = ArrayConstants.emptyServerLevelArray; -+ private @Nullable ServerLevel overworld; -+ // Gale end - base thread pool - optimize server levels - private PlayerList playerList; - private volatile boolean running; - private volatile boolean isRestarting = false; // Paper - flag to signify we're attempting to restart -@@ -259,10 +260,114 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop static, final -> non-final (but still effectively final) -+ // Gale start - base thread pool - make fields volatile -+ private volatile long nextTickTime; -+ private volatile long delayedTasksMaxNextTickTime; -+ // Gale end - base thread pool - make fields volatile -+ -+ // Gale start - base thread pool -+ -+ public static volatile long nextTickStartNanoTime; -+ public static volatile long delayedTasksMaxNextTickNanoTime; -+ -+ /** -+ * Sets {@link #nextTickTime}, and sets {@link #nextTickStartNanoTime} accordingly. -+ */ -+ private void setNextTickTime(long nextTickTime) { -+ this.nextTickTime = nextTickTime; -+ /* -+ Add 10000 nanoseconds, to make sure the currentTime() >= nextTickTime check will be true after this moment -+ regardless of the nanosecond granularity of the Condition#await function, which is probably somewhere around -+ 26 nanoseconds. -+ */ -+ nextTickStartNanoTime = 1_000_000L * this.nextTickTime + 10_000L; -+ } -+ -+ /** -+ * Sets {@link #delayedTasksMaxNextTickTime}, and sets {@link #delayedTasksMaxNextTickNanoTime} accordingly. -+ * -+ * @see #setNextTickTime -+ */ -+ private void setDelayedTasksMaxNextTickTime(long delayedTasksMaxNextTickTime) { -+ this.delayedTasksMaxNextTickTime = delayedTasksMaxNextTickTime; -+ delayedTasksMaxNextTickNanoTime = 1_000_000L * this.delayedTasksMaxNextTickTime + 10_000L; -+ } -+ -+ /** -+ * Whether to skip the next call to {@link #mayHaveDelayedTasks()} and simply return true. -+ * This is typically set to true when a new task is added to a queue with tasks that count as potentially -+ * delayed tasks, or when an element from such a queue is successfully polled (even though it may afterwards be -+ * empty, it seems better to simply poll again next time, rather than perform the full {@link #mayHaveDelayedTasks()} -+ * check that loops over all queues). -+ */ -+ public static volatile boolean nextTimeAssumeWeMayHaveDelayedTasks; -+ -+ /** -+ * Whether the value of {@link #lastComputedMayHaveDelayedTasks} should be assumed to be correct. -+ */ -+ public static volatile boolean mayHaveDelayedTasksIsCurrentlyComputed; -+ -+ /** -+ * The cached last computed correct (except for potential race condition mistakes in the computation) -+ * value of {@link #mayHaveDelayedTasks()}. -+ */ -+ public static volatile boolean lastComputedMayHaveDelayedTasks; -+ -+ /** -+ * Whether the server is currently in spare time after a tick. -+ * This is set to true by the {@link #serverThread} when entering the spare time phase, -+ * either at the end of a tick, or at the start of one (if it occurred too early), and set to false after -+ * the corresponding {@link #managedBlock} call. -+ */ -+ public static volatile boolean isInSpareTime = false; -+ -+ /** -+ * Whether the server is currently waiting for the next tick, which is one of the cases where -+ * {@link #isInSpareTime} is true. Specifically, the other case where {@link #isInSpareTime} is true is -+ * while {@link #isOversleep} is true. -+ */ -+ public static volatile boolean isWaitingUntilNextTick = false; -+ -+ /** -+ * A potentially out-of-date value indicating whether {@link #isInSpareTime} is true -+ * and {@link #haveTime()} is false and {@link #blockingCount} is 0. -+ * This should be updated just in time before it is potentially needed. -+ */ -+ public static volatile boolean isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking = false; -+ -+ /** -+ * The stop condition provided to the current call of {@link #managedBlock}, or null if no {@link #managedBlock} -+ * call is ongoing. -+ */ -+ public static volatile @Nullable BooleanSupplier currentManagedBlockStopCondition; -+ -+ /** -+ * Whether the {@link #currentManagedBlockStopCondition} has become true -+ * during the last {@link #managedBlock} call. -+ */ -+ public static volatile boolean currentManagedBlockStopConditionHasBecomeTrue = false; -+ -+ public static void signalServerThreadIfCurrentManagedBlockStopConditionBecameTrue() { -+ if (currentManagedBlockStopConditionHasBecomeTrue) { -+ // We already signalled the thread -+ return; -+ } -+ var managedBlockStopCondition = currentManagedBlockStopCondition; -+ if (managedBlockStopCondition == null) { -+ // There is no ongoing managedBlock cal -+ return; -+ } -+ if (!managedBlockStopCondition.getAsBoolean()) { -+ // The stop condition is not true -+ return; -+ } -+ currentManagedBlockStopConditionHasBecomeTrue = true; -+ serverThread.signal(null); -+ } -+ -+ // Gale start - base thread pool -+ - private final PackRepository packRepository; - private final ServerScoreboard scoreboard; - @Nullable -@@ -291,7 +396,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); - public int autosavePeriod; - public Commands vanillaCommandDispatcher; -- public boolean forceTicks; // Paper -+ public volatile boolean forceTicks; // Paper // Gale - base thread pool - make fields volatile - // CraftBukkit end - // Spigot start - public static final int TPS = 20; -@@ -307,9 +412,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function serverFactory) { -+ public static S spin(Function serverFactory) { // Gale - base thread pool - AtomicReference atomicreference = new AtomicReference(); -- Thread thread = new io.papermc.paper.util.TickThread(() -> { // Paper - rewrite chunk system -+ OriginalServerThread thread = new OriginalServerThread(() -> { // Paper - rewrite chunk system // Gale - base thread pool - ((MinecraftServer) atomicreference.get()).runServer(); - }, "Server thread"); - -@@ -328,15 +433,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop holdergetter = this.registries.compositeAccess().registryOrThrow(Registries.BLOCK).asLookup().filterFeatures(this.worldData.enabledFeatures()); - - this.structureTemplateManager = new StructureTemplateManager(worldstem.resourceManager(), convertable_conversionsession, datafixer, holdergetter); -- this.serverThread = thread; -+ // Gale start - base thread pool -+ serverThread = thread; -+ BaseThreadActivation.callForUpdate(); -+ // Gale end - base thread pool - this.executor = Util.backgroundExecutor(); - } - // CraftBukkit start -@@ -602,7 +713,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop public - public volatile boolean hasFullyShutdown = false; // Paper - private boolean hasLoggedStop = false; // Paper - private final Object stopLock = new Object(); -@@ -919,8 +1031,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop public - // Paper start - if (this.forceTicks) { - return true; -@@ -1254,13 +1402,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop public - private boolean canOversleep() { -- return this.mayHaveDelayedTasks && Util.getMillis() < this.delayedTasksMaxNextTickTime; -+ return Util.getMillis() < this.delayedTasksMaxNextTickTime && mayHaveDelayedTasks(); // Gale - base thread pool - } - - private boolean canSleepForTickNoOversleep() { -@@ -1269,7 +1417,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { - return !this.canSleepForTickNoOversleep(); // Paper - move oversleep into full server tick -+ // Gale start - base thread pool - }); -+ isInSpareTime = false; -+ isWaitingUntilNextTick = false; -+ // Gale end - base thread pool - lastTickOversleepTime = (System.nanoTime() - tickOversleepStart) / 1000000L; // Gale - YAPFA - last tick time - } - -- @Override -- public TickTask wrapRunnable(Runnable runnable) { -- // Paper start - anything that does try to post to main during watchdog crash, run on watchdog -- if (this.hasStopped && Thread.currentThread().equals(shutdownThread)) { -- runnable.run(); -- runnable = () -> {}; -- } -- // Paper end -- return new TickTask(this.tickCount, runnable); -- } -- -- protected boolean shouldRun(TickTask ticktask) { -- return ticktask.getTick() + 3 < this.tickCount || this.haveTime(); -- } -- -- @Override -- public boolean pollTask() { -- boolean flag = this.pollTaskInternal(); -- -- this.mayHaveDelayedTasks = flag; -- return flag; -- } -- -- private boolean pollTaskInternal() { -- if (super.pollTask()) { -- this.executeMidTickTasks(); // Paper - execute chunk tasks mid tick -- return true; -- } else { -- boolean ret = false; // Paper - force execution of all worlds, do not just bias the first -- if (this.haveTime()) { -- Iterator iterator = this.getAllLevels().iterator(); -- -- while (iterator.hasNext()) { -- ServerLevel worldserver = (ServerLevel) iterator.next(); -- -- if (worldserver.getChunkSource().pollTask()) { -- ret = true; // Paper - force execution of all worlds, do not just bias the first -- } -- } -- } -- -- return ret; // Paper - force execution of all worlds, do not just bias the first -- } -- } -- -- public void doRunTask(TickTask ticktask) { // CraftBukkit - decompile error -- super.doRunTask(ticktask); -- } -- - private Optional loadStatusIcon() { - Optional optional = Optional.of(this.getFile("server-icon.png").toPath()).filter((path) -> { - return Files.isRegularFile(path, new LinkOption[0]); -@@ -1376,14 +1483,19 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { - return !this.canOversleep(); -+ // Gale start - base thread pool - }); -+ isInSpareTime = false; -+ // Gale end - base thread pool - isOversleep = false;MinecraftTimings.serverOversleep.stopTiming(); - // Paper end - new com.destroystokyo.paper.event.server.ServerTickStartEvent(this.tickCount+1).callEvent(); // Paper - - ++this.tickCount; -+ ScheduledServerThreadTaskQueues.shiftTasksForNextTick(); // Gale - base thread pool - this.tickChildren(shouldKeepTicking); - if (i - this.lastServerStatus >= 5000000000L) { - this.lastServerStatus = i; -@@ -1401,7 +1513,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0) { - this.playerList.saveAll(playerSaveInterval); - } -- for (ServerLevel level : this.getAllLevels()) { -+ for (ServerLevel level : this.getAllLevelsArray()) { // Gale - base thread pool - optimize server levels - if (level.paperConfig().chunks.autoSaveInterval.value() > 0) { - level.saveIncrementally(fullSave); - } -@@ -1413,7 +1525,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper -@@ -1592,7 +1702,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels); - newLevels.put(level.dimension(), level); - this.levels = Collections.unmodifiableMap(newLevels); -+ // Gale start - base thread pool - optimize server levels -+ this.levelArray = newLevels.values().toArray(ServerLevel[]::new); -+ for (int i = 0; i < this.levelArray.length; i++) { -+ this.levelArray[i].serverLevelArrayIndex = i; -+ } -+ this.overworld = null; -+ // Gale end - base thread pool - optimize server levels - } - - public void removeLevel(ServerLevel level) { -@@ -1621,6 +1743,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels); - newLevels.remove(level.dimension()); - this.levels = Collections.unmodifiableMap(newLevels); -+ // Gale start - base thread pool - optimize server levels -+ level.serverLevelArrayIndex = -1; -+ this.levelArray = newLevels.values().toArray(ServerLevel[]::new); -+ for (int i = 0; i < this.levelArray.length; i++) { -+ this.levelArray[i].serverLevelArrayIndex = i; -+ } -+ this.overworld = null; -+ // Gale end - base thread pool - optimize server levels - } - // CraftBukkit end - -@@ -1628,8 +1758,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop getAllLevels() { -- return this.levels.values(); -+ return this.levels == null ? Collections.emptyList() : this.levels.values(); // Gale - base thread pool - } - - public String getServerVersion() { -@@ -1749,10 +1885,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0) { -+ return; -+ } -+ // Gale end - base thread pool - org.spigotmc.AsyncCatcher.catchOp("mid tick chunk task execution"); - long startTime = System.nanoTime(); - if ((startTime - lastMidTickExecute) <= CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME || (startTime - lastMidTickExecuteFailure) <= TASK_EXECUTION_FAILURE_BACKOFF) { -diff --git a/src/main/java/net/minecraft/server/commands/TimeCommand.java b/src/main/java/net/minecraft/server/commands/TimeCommand.java -index f0a7a8df3caa2ea765bb0a87cfede71d0995d276..16f3475b059d2b6b85d2b342e84ab32de8e86ac0 100644 ---- a/src/main/java/net/minecraft/server/commands/TimeCommand.java -+++ b/src/main/java/net/minecraft/server/commands/TimeCommand.java -@@ -51,10 +51,11 @@ public class TimeCommand { - } - - public static int setTime(CommandSourceStack source, int time) { -- Iterator iterator = io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels().iterator() : com.google.common.collect.Iterators.singletonIterator(source.getLevel()); // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in // Paper - add config option for spigot's change -+ // Gale start - base thread pool - optimize server levels -+ ServerLevel[] worldservers = io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevelsArray() : new ServerLevel[]{source.getLevel()}; // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in // Paper - add config option for spigot's change - -- while (iterator.hasNext()) { -- ServerLevel worldserver = (ServerLevel) iterator.next(); -+ for (ServerLevel worldserver : worldservers) { -+ // Gale end - base thread pool - optimize server levels - - // CraftBukkit start - TimeSkipEvent event = new TimeSkipEvent(worldserver.getWorld(), TimeSkipEvent.SkipReason.COMMAND, time - worldserver.getDayTime()); -@@ -70,10 +71,11 @@ public class TimeCommand { - } - - public static int addTime(CommandSourceStack source, int time) { -- Iterator iterator = io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels().iterator() : com.google.common.collect.Iterators.singletonIterator(source.getLevel()); // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in // Paper - add config option for spigot's change -+ // Gale start - base thread pool - optimize server levels -+ ServerLevel[] worldservers = io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevelsArray() : new ServerLevel[]{source.getLevel()}; // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in // Paper - add config option for spigot's change - -- while (iterator.hasNext()) { -- ServerLevel worldserver = (ServerLevel) iterator.next(); -+ for (ServerLevel worldserver : worldservers) { -+ // Gale end - base thread pool - optimize server levels - - // CraftBukkit start - TimeSkipEvent event = new TimeSkipEvent(worldserver.getWorld(), TimeSkipEvent.SkipReason.COMMAND, time); -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index 666114daf9e9a3f9f9e0779a3a40dfac09c80d60..1141b3a88e2eb1baa705b4f781353df0305a7c85 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -49,6 +49,7 @@ import net.minecraft.world.level.block.entity.SkullBlockEntity; - import net.minecraft.world.level.storage.LevelStorageSource; - import org.galemc.gale.command.GaleCommands; - import org.galemc.gale.configuration.GaleGlobalConfiguration; -+import org.galemc.gale.executor.thread.OriginalServerThread; - import org.galemc.gale.util.CPUCoresEstimation; - import org.slf4j.Logger; - -@@ -82,7 +83,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - private final TextFilterClient textFilterClient; - - // CraftBukkit start - Signature changed -- public DedicatedServer(joptsimple.OptionSet options, WorldLoader.DataLoadContext worldLoader, Thread thread, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PackRepository resourcepackrepository, WorldStem worldstem, DedicatedServerSettings dedicatedserversettings, DataFixer datafixer, Services services, ChunkProgressListenerFactory worldloadlistenerfactory) { -+ public DedicatedServer(joptsimple.OptionSet options, WorldLoader.DataLoadContext worldLoader, OriginalServerThread thread, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PackRepository resourcepackrepository, WorldStem worldstem, DedicatedServerSettings dedicatedserversettings, DataFixer datafixer, Services services, ChunkProgressListenerFactory worldloadlistenerfactory) { // Gale - base thread pool - super(options, worldLoader, thread, convertable_conversionsession, resourcepackrepository, worldstem, Proxy.NO_PROXY, datafixer, services, worldloadlistenerfactory); - // CraftBukkit end - this.settings = dedicatedserversettings; -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index df4db98618c6c9261b4ec8e2987c4ed26af4bd4b..83a57b9bc59063ed8299f98bc33e14b57f2ea0de 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -48,6 +48,9 @@ import net.minecraft.world.level.storage.DimensionDataStorage; - import net.minecraft.world.level.storage.LevelData; - import net.minecraft.world.level.storage.LevelStorageSource; - import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper -+import org.galemc.gale.executor.lock.YieldingLock; -+import org.galemc.gale.executor.queue.BaseTaskQueues; -+import org.galemc.gale.executor.thread.AbstractYieldingThread; - - public class ServerChunkCache extends ChunkSource { - -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 0dfc703c3a1101da4b817227e2c41d467c37ae88..feeca393f960210a62740da053d21fecd62b0252 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -22,6 +22,8 @@ import java.io.Writer; - import java.nio.file.Files; - import java.nio.file.Path; - import java.util.ArrayList; -+import java.util.Arrays; -+import java.util.Collections; - import java.util.Comparator; - import java.util.Iterator; - import java.util.List; -@@ -159,6 +161,8 @@ import net.minecraft.world.phys.shapes.BooleanOp; - import net.minecraft.world.phys.shapes.Shapes; - import net.minecraft.world.phys.shapes.VoxelShape; - import net.minecraft.world.ticks.LevelTicks; -+import org.galemc.gale.executor.annotation.Access; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; - import org.slf4j.Logger; - import org.bukkit.Bukkit; - import org.bukkit.Location; -@@ -187,6 +191,10 @@ public class ServerLevel extends Level implements WorldGenLevel { - private static final int MAX_SCHEDULED_TICKS_PER_TICK = 65536; - final List players; - public final ServerChunkCache chunkSource; -+ // Gale start - base thread pool -+ @AnyThreadSafe(Access.READ) -+ public volatile int serverLevelArrayIndex; -+ // Gale end - base thread pool - private final MinecraftServer server; - public final PrimaryLevelData serverLevelData; // CraftBukkit - type - final EntityTickList entityTickList; -@@ -2563,7 +2571,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - // Spigot start - if ( entity instanceof Player ) - { -- com.google.common.collect.Streams.stream( ServerLevel.this.getServer().getAllLevels() ).map( ServerLevel::getDataStorage ).forEach( (worldData) -> -+ Arrays.stream( ServerLevel.this.getServer().getAllLevelsArray() ).map( ServerLevel::getDataStorage ).forEach( (worldData) -> // Gale - base thread pool - optimize server levels - { - for (Object o : worldData.cache.values() ) - { -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 5d61d3ead44175654f39760f9a7fdd4914b002b3..7c3cc5cdcf352f50eab0dda110399a60a758e269 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -187,8 +187,9 @@ import net.minecraft.world.phys.shapes.BooleanOp; - import net.minecraft.world.phys.shapes.Shapes; - import net.minecraft.world.phys.shapes.VoxelShape; - import org.bukkit.craftbukkit.util.permissions.CraftDefaultPermissions; --import org.apache.commons.lang3.StringUtils; - import org.galemc.gale.configuration.GaleGlobalConfiguration; -+import org.galemc.gale.executor.queue.BaseTaskQueues; -+import org.galemc.gale.executor.queue.ScheduledServerThreadTaskQueues; - import org.slf4j.Logger; - - // CraftBukkit start -@@ -564,7 +565,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - - Objects.requireNonNull(this.connection); - // CraftBukkit - Don't wait -- minecraftserver.scheduleOnMain(networkmanager::handleDisconnection); // Paper -+ ScheduledServerThreadTaskQueues.add(networkmanager::handleDisconnection, ScheduledServerThreadTaskQueues.HANDLE_DISCONNECT_TASK_MAX_DELAY); // Paper // Gale - base thread pool - } - - private CompletableFuture filterTextPacket(T text, BiFunction> filterer) { -@@ -903,13 +904,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - // PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); // Paper - run this async - // CraftBukkit start - if (this.chatSpamTickCount.addAndGet(io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.tabSpamIncrement) > io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.tabSpamLimit && !this.server.getPlayerList().isOp(this.player.getGameProfile())) { // Paper start - split and make configurable -- server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause // Gale - JettPack - reduce array allocations -+ ScheduledServerThreadTaskQueues.add(() -> this.disconnect(Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM), ScheduledServerThreadTaskQueues.KICK_FOR_COMMAND_PACKET_SPAM_TASK_MAX_DELAY); // Paper - kick event cause // Gale - JettPack - reduce array allocations // Gale - base thread pool - return; - } - // Paper start - String str = packet.getCommand(); int index = -1; - if (str.length() > 64 && ((index = str.indexOf(' ')) == -1 || index >= 64)) { -- server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause // Gale - JettPack - reduce array allocations -+ ScheduledServerThreadTaskQueues.add(() -> this.disconnect(Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM), ScheduledServerThreadTaskQueues.KICK_FOR_COMMAND_PACKET_SPAM_TASK_MAX_DELAY); // Paper - kick event cause // Gale - JettPack - reduce array allocations // Gale - base thread pool - return; - } - // Paper end -@@ -934,7 +935,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - if (!event.isHandled()) { - if (!event.isCancelled()) { - -- this.server.scheduleOnMain(() -> { // This needs to be on main -+ ScheduledServerThreadTaskQueues.add(() -> { // This needs to be on main // Gale - base thread pool - ParseResults parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack()); - - this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> { -@@ -945,7 +946,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestEvent.getSuggestions())); - // Paper end - Brigadier API - }); -- }); -+ }, ScheduledServerThreadTaskQueues.SEND_COMMAND_COMPLETION_SUGGESTIONS_TASK_MAX_DELAY); // Gale - base thread pool - } - } else if (!completions.isEmpty()) { - final com.mojang.brigadier.suggestion.SuggestionsBuilder builder0 = new com.mojang.brigadier.suggestion.SuggestionsBuilder(command, stringreader.getTotalLength()); -@@ -1259,7 +1260,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - int byteLength = testString.getBytes(java.nio.charset.StandardCharsets.UTF_8).length; - if (byteLength > 256 * 4) { - ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " tried to send a book with with a page too large!"); -- server.scheduleOnMain(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION)); // Paper - kick event cause -+ ScheduledServerThreadTaskQueues.add(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION), ScheduledServerThreadTaskQueues.KICK_FOR_BOOK_TOO_LARGE_PACKET_TASK_MAX_DELAY); // Paper - kick event cause // Gale - base thread pool - return; - } - byteTotal += byteLength; -@@ -1282,14 +1283,14 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - - if (byteTotal > byteAllowed) { - ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " tried to send too large of a book. Book Size: " + byteTotal + " - Allowed: "+ byteAllowed + " - Pages: " + pageList.size()); -- server.scheduleOnMain(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION)); // Paper - kick event cause -+ ScheduledServerThreadTaskQueues.add(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION), ScheduledServerThreadTaskQueues.KICK_FOR_BOOK_TOO_LARGE_PACKET_TASK_MAX_DELAY); // Paper - kick event cause // Gale - base thread pool - return; - } - } - // Paper end - // CraftBukkit start - if (this.lastBookTick + 20 > MinecraftServer.currentTick) { -- server.scheduleOnMain(() -> this.disconnect("Book edited too quickly!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION)); // Paper - kick event cause // Paper - Also ensure this is called on main -+ ScheduledServerThreadTaskQueues.add(() -> this.disconnect("Book edited too quickly!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION), ScheduledServerThreadTaskQueues.KICK_FOR_EDITING_BOOK_TOO_QUICKLY_TASK_MAX_DELAY); // Paper - kick event cause // Paper - Also ensure this is called on main // Gale - base thread pool - return; - } - this.lastBookTick = MinecraftServer.currentTick; -@@ -2081,10 +2082,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - public void handleTeleportToEntityPacket(ServerboundTeleportToEntityPacket packet) { - PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); - if (this.player.isSpectator()) { -- Iterator iterator = this.server.getAllLevels().iterator(); -- -- while (iterator.hasNext()) { -- ServerLevel worldserver = (ServerLevel) iterator.next(); -+ for (ServerLevel worldserver : this.server.getAllLevelsArray()) { // Gale - base thread pool - optimize server levels - Entity entity = packet.getEntity(worldserver); - - if (entity != null) { -@@ -2236,9 +2234,9 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - } - // CraftBukkit end - if (ServerGamePacketListenerImpl.isChatMessageIllegal(packet.message())) { -- this.server.scheduleOnMain(() -> { // Paper - push to main for event firing -+ ScheduledServerThreadTaskQueues.add(() -> { // Paper - push to main for event firing // Gale - base thread pool - this.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper - add cause -- }); // Paper - push to main for event firing -+ }, ScheduledServerThreadTaskQueues.KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY); // Paper - push to main for event firing // Gale - base thread pool - } else { - Optional optional = this.tryHandleChat(packet.message(), packet.timeStamp(), packet.lastSeenMessages()); - -@@ -2272,9 +2270,9 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - @Override - public void handleChatCommand(ServerboundChatCommandPacket packet) { - if (ServerGamePacketListenerImpl.isChatMessageIllegal(packet.command())) { -- this.server.scheduleOnMain(() -> { // Paper - push to main for event firing -+ ScheduledServerThreadTaskQueues.add(() -> { // Paper - push to main for event firing // Gale - base thread pool - this.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper -- }); // Paper - push to main for event firing -+ }, ScheduledServerThreadTaskQueues.KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY); // Paper - push to main for event firing // Gale - base thread pool - } else { - Optional optional = this.tryHandleChat(packet.command(), packet.timeStamp(), packet.lastSeenMessages()); - -@@ -2362,9 +2360,9 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - private Optional tryHandleChat(String message, Instant timestamp, LastSeenMessages.Update acknowledgment) { - if (!this.updateChatOrder(timestamp)) { - if (GaleGlobalConfiguration.get().logToConsole.chat.outOfOrderMessageWarning) ServerGamePacketListenerImpl.LOGGER.warn("{} sent out-of-order chat: '{}': {} > {}", this.player.getName().getString(), message, this.lastChatTimeStamp.get().getEpochSecond(), timestamp.getEpochSecond()); // Paper // Gale - do not log out-of-order message warnings -- this.server.scheduleOnMain(() -> { // Paper - push to main -- this.disconnect(Component.translatable("multiplayer.disconnect.out_of_order_chat"), org.bukkit.event.player.PlayerKickEvent.Cause.OUT_OF_ORDER_CHAT); // Paper - kick event ca -- }); // Paper - push to main -+ ScheduledServerThreadTaskQueues.add(() -> { // Paper - push to main // Gale - base thread pool -+ this.disconnect(Component.translatable("multiplayer.disconnect.out_of_order_chat"), org.bukkit.event.player.PlayerKickEvent.Cause.OUT_OF_ORDER_CHAT); // Paper - kick event cause -+ }, ScheduledServerThreadTaskQueues.KICK_FOR_OUT_OF_ORDER_CHAT_PACKET_TASK_MAX_DELAY); // Paper - push to main // Gale - base thread pool - return Optional.empty(); - } else { - Optional optional = this.unpackAndApplyLastSeen(acknowledgment); -@@ -3327,7 +3325,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - // Paper start - if (!org.bukkit.Bukkit.isPrimaryThread()) { - if (recipeSpamPackets.addAndGet(io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.recipeSpamIncrement) > io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.recipeSpamLimit) { -- server.scheduleOnMain(() -> this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM)); // Paper - kick event cause // Gale - JettPack - reduce array allocations -+ ScheduledServerThreadTaskQueues.add(() -> this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM), ScheduledServerThreadTaskQueues.KICK_FOR_RECIPE_PACKET_SPAM_TASK_MAX_DELAY); // Paper - kick event cause // Gale - JettPack - reduce array allocations // Gale - base thread pool - return; - } - } -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index eb90b375c589076afc454bd80f013384df090b11..f70d5167fad762c858f10ba19636ddb829dc9538 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -15,7 +15,6 @@ import java.net.SocketAddress; - import java.nio.file.Path; - import java.text.SimpleDateFormat; - import java.time.Instant; --import java.util.ArrayList; - import java.util.Arrays; - import java.util.Collection; - import java.util.EnumSet; -@@ -106,10 +105,10 @@ import net.minecraft.world.scores.PlayerTeam; - import net.minecraft.world.scores.Scoreboard; // Paper - import net.minecraft.world.scores.Team; - import org.galemc.gale.configuration.GaleGlobalConfiguration; -+import org.galemc.gale.executor.queue.ScheduledServerThreadTaskQueues; - import org.slf4j.Logger; - - // CraftBukkit start --import java.util.stream.Collectors; - import net.minecraft.server.dedicated.DedicatedServer; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.server.level.ServerPlayer; -@@ -305,6 +304,58 @@ public abstract class PlayerList { - player.getRecipeBook().sendInitialRecipeBook(player); - this.updateEntireScoreboard(worldserver1.getScoreboard(), player); - this.server.invalidateStatus(); -+/* // Gale - base thread pool - this patch was removed from Paper but might be useful later -+ // Paper start - async load spawn in chunk -+ ServerLevel finalWorldserver = worldserver1; -+ finalWorldserver.pendingLogin.add(player); -+ int chunkX = loc.getBlockX() >> 4; -+ int chunkZ = loc.getBlockZ() >> 4; -+ final net.minecraft.world.level.ChunkPos pos = new net.minecraft.world.level.ChunkPos(chunkX, chunkZ); -+ net.minecraft.server.level.ChunkMap playerChunkMap = worldserver1.getChunkSource().chunkMap; -+ net.minecraft.server.level.DistanceManager distanceManager = playerChunkMap.distanceManager; -+ io.papermc.paper.chunk.system.ChunkSystem.scheduleTickingState( -+ worldserver1, chunkX, chunkZ, net.minecraft.server.level.ChunkHolder.FullChunkStatus.ENTITY_TICKING, true, -+ ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHEST, -+ (chunk) -> { -+ ScheduledServerThreadTaskQueues.add(() -> { // Gale - base thread pool -+ try { -+ if (!playerconnection.connection.isConnected()) { -+ return; -+ } -+ PlayerList.this.postChunkLoadJoin( -+ player, finalWorldserver, connection, playerconnection, -+ nbttagcompound, s1, lastKnownName -+ ); -+ distanceManager.addTicket(net.minecraft.server.level.TicketType.LOGIN, pos, 31, pos.toLong()); -+ } finally { -+ finalWorldserver.pendingLogin.remove(player); -+ } -+ }, ScheduledServerThreadTaskQueues.POST_CHUNK_LOAD_JOIN_TASK_MAX_DELAY); // Gale - base thread pool -+ } -+ ); -+ } -+ -+ public ServerPlayer getActivePlayer(UUID uuid) { -+ ServerPlayer player = this.playersByUUID.get(uuid); -+ return player != null ? player : pendingPlayers.get(uuid); -+ } -+ -+ void disconnectPendingPlayer(ServerPlayer entityplayer) { -+ Component msg = Component.translatable("multiplayer.disconnect.duplicate_login"); -+ entityplayer.networkManager.send(new net.minecraft.network.protocol.game.ClientboundDisconnectPacket(msg), net.minecraft.network.PacketSendListener.thenRun(() -> { -+ entityplayer.networkManager.disconnect(msg); -+ entityplayer.networkManager = null; -+ })); -+ } -+ -+ private void postChunkLoadJoin(ServerPlayer player, ServerLevel worldserver1, Connection networkmanager, ServerGamePacketListenerImpl playerconnection, CompoundTag nbttagcompound, String s1, String s) { -+ pendingPlayers.remove(player.getUUID(), player); -+ if (!networkmanager.isConnected()) { -+ return; -+ } -+ player.didPlayerJoinEvent = true; -+ // Paper end -+*/ // Gale - base thread pool - this patch was removed from Paper but might be useful later - MutableComponent ichatmutablecomponent; - - if (player.getGameProfile().getName().equalsIgnoreCase(s)) { -@@ -1539,10 +1590,8 @@ public abstract class PlayerList { - public void setViewDistance(int viewDistance) { - this.viewDistance = viewDistance; - //this.broadcastAll(new ClientboundSetChunkCacheRadiusPacket(viewDistance)); // Paper - move into setViewDistance -- Iterator iterator = this.server.getAllLevels().iterator(); - -- while (iterator.hasNext()) { -- ServerLevel worldserver = (ServerLevel) iterator.next(); -+ for (ServerLevel worldserver : this.server.getAllLevelsArray()) { // Gale - base thread pool - optimize server levels - - if (worldserver != null) { - worldserver.getChunkSource().setViewDistance(viewDistance); -@@ -1554,10 +1603,8 @@ public abstract class PlayerList { - public void setSimulationDistance(int simulationDistance) { - this.simulationDistance = simulationDistance; - //this.broadcastAll(new ClientboundSetSimulationDistancePacket(simulationDistance)); // Paper - handled by playerchunkloader -- Iterator iterator = this.server.getAllLevels().iterator(); - -- while (iterator.hasNext()) { -- ServerLevel worldserver = (ServerLevel) iterator.next(); -+ for (ServerLevel worldserver : this.server.getAllLevelsArray()) { // Gale - base thread pool - optimize server levels - - if (worldserver != null) { - worldserver.getChunkSource().setSimulationDistance(simulationDistance); -diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -index 83701fbfaa56a232593ee8f11a3afb8941238bfa..392e7b4a89669f16b32043b65b69e6593d17f10e 100644 ---- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -+++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -@@ -6,17 +6,18 @@ import com.mojang.logging.LogUtils; - import java.util.List; - import java.util.Queue; - import java.util.concurrent.CompletableFuture; --import java.util.concurrent.Executor; - import java.util.concurrent.locks.LockSupport; - import java.util.function.BooleanSupplier; - import java.util.function.Supplier; -+ - import net.minecraft.util.profiling.metrics.MetricCategory; - import net.minecraft.util.profiling.metrics.MetricSampler; - import net.minecraft.util.profiling.metrics.MetricsRegistry; - import net.minecraft.util.profiling.metrics.ProfilerMeasured; -+import org.galemc.gale.executor.AbstractBlockableEventLoop; - import org.slf4j.Logger; - --public abstract class BlockableEventLoop implements ProfilerMeasured, ProcessorHandle, Executor { -+public abstract class BlockableEventLoop implements ProfilerMeasured, ProcessorHandle, AbstractBlockableEventLoop { // Gale - base thread pool - private final String name; - private static final Logger LOGGER = LogUtils.getLogger(); - private final Queue pendingRunnables = Queues.newConcurrentLinkedQueue(); -@@ -31,6 +32,7 @@ public abstract class BlockableEventLoop implements Profiler - - protected abstract boolean shouldRun(R task); - -+ @Override // Gale - base thread pool - public boolean isSameThread() { - return Thread.currentThread() == this.getRunningThread(); - } -@@ -45,6 +47,12 @@ public abstract class BlockableEventLoop implements Profiler - return this.pendingRunnables.size(); - } - -+ // Gale start - base thread pool -+ public boolean hasPendingTasks() { -+ return !this.pendingRunnables.isEmpty(); -+ } -+ // Gale end - base thread pool -+ - @Override - public String name() { - return this.name; -@@ -102,6 +110,7 @@ public abstract class BlockableEventLoop implements Profiler - - } - -+ @Override // Gale - base thread pool - public void executeIfPossible(Runnable runnable) { - this.execute(runnable); - } -diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -index 757f1bcaa7919df037507f9e270424bb21e98325..87ea4fb3f0e5e3147a1e832b03972b7455adcf44 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -@@ -110,7 +110,7 @@ public abstract class Projectile extends Entity implements TraceableEntity { - this.cachedOwner = ((ServerLevel) this.level).getEntity(this.ownerUUID); - // Paper start - check all worlds - if (this.cachedOwner == null) { -- for (final ServerLevel level : this.level.getServer().getAllLevels()) { -+ for (final ServerLevel level : this.level.getServer().getAllLevelsArray()) { // Gale - base thread pool - optimize server levels - if (level == this.level) continue; - final Entity entity = level.getEntity(this.ownerUUID); - if (entity != null) { -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index d175d019c865bf9ecc5641d39fb1a29aebde662c..841492478d64a10e722ec5ac98b5a8606a65669a 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -986,7 +986,7 @@ public final class CraftServer implements Server { - org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot - this.console.paperConfigurations.reloadConfigs(this.console); - this.console.galeConfigurations.reloadConfigs(this.console); // Gale - Gale configuration -- for (ServerLevel world : this.console.getAllLevels()) { -+ for (ServerLevel world : this.console.getAllLevelsArray()) { // Gale - base thread pool - optimize server levels - // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty - world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) - -@@ -1167,7 +1167,7 @@ public final class CraftServer implements Server { - - @Override - public World createWorld(WorldCreator creator) { -- Preconditions.checkState(this.console.getAllLevels().iterator().hasNext(), "Cannot create additional worlds on STARTUP"); -+ Preconditions.checkState(this.console.getAllLevelsArray().length > 0, "Cannot create additional worlds on STARTUP"); // Gale - base thread pool - optimize server levels - //Preconditions.checkState(!this.console.isIteratingOverLevels, "Cannot create a world while worlds are being ticked"); // Paper - Cat - Temp disable. We'll see how this goes. - Validate.notNull(creator, "Creator may not be null"); - -@@ -2526,7 +2526,7 @@ public final class CraftServer implements Server { - public Entity getEntity(UUID uuid) { - Validate.notNull(uuid, "UUID cannot be null"); - -- for (ServerLevel world : this.getServer().getAllLevels()) { -+ for (ServerLevel world : this.getServer().getAllLevelsArray()) { // Gale - base thread pool - optimize server levels - net.minecraft.world.entity.Entity entity = world.getEntity(uuid); - if (entity != null) { - return entity.getBukkitEntity(); -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 8f0234296397ca2d4a607dcea6093c6c606dc7d2..1c2220ff497db66a48ae47cff9103c424ff37bc8 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -5,7 +5,6 @@ import com.google.common.base.Predicates; - import com.google.common.collect.ImmutableList; - import com.google.common.collect.ImmutableMap; - import com.mojang.datafixers.util.Pair; --import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; - import it.unimi.dsi.fastutil.longs.Long2ObjectMap; - import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; - import java.io.File; -@@ -20,7 +19,6 @@ import java.util.Objects; - import java.util.Random; - import java.util.Set; - import java.util.UUID; --import java.util.concurrent.ExecutionException; - import java.util.function.Predicate; - import java.util.stream.Collectors; - import net.minecraft.core.BlockPos; -@@ -117,7 +115,6 @@ import org.bukkit.entity.TippedArrow; - import org.bukkit.entity.Trident; - import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; - import org.bukkit.event.weather.LightningStrikeEvent; --import org.bukkit.event.world.SpawnChangeEvent; - import org.bukkit.event.world.TimeSkipEvent; - import org.bukkit.generator.BiomeProvider; - import org.bukkit.generator.BlockPopulator; -@@ -137,6 +134,7 @@ import org.bukkit.util.Consumer; - import org.bukkit.util.RayTraceResult; - import org.bukkit.util.StructureSearchResult; - import org.bukkit.util.Vector; -+import org.galemc.gale.executor.queue.ScheduledServerThreadTaskQueues; - import org.jetbrains.annotations.NotNull; - - public class CraftWorld extends CraftRegionAccessor implements World { -@@ -2408,11 +2406,11 @@ public class CraftWorld extends CraftRegionAccessor implements World { - java.util.concurrent.CompletableFuture ret = new java.util.concurrent.CompletableFuture<>(); - - io.papermc.paper.chunk.system.ChunkSystem.scheduleChunkLoad(this.getHandle(), x, z, gen, ChunkStatus.FULL, true, priority, (c) -> { -- net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> { -+ ScheduledServerThreadTaskQueues.add(() -> { // Gale - base thread pool - net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)c; - if (chunk != null) this.addTicket(x, z); // Paper - ret.complete(chunk == null ? null : new CraftChunk(chunk)); -- }); -+ }, ScheduledServerThreadTaskQueues.COMPLETE_CHUNK_FUTURE_TASK_MAX_DELAY); // Gale - base thread pool - }); - - return ret; -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index 56c75029a94e8812c9e0ce5375aaa7cbcda90b87..7b4f51358d36a52e90d2cda25cd884772f62d65b 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -191,6 +191,7 @@ import org.bukkit.plugin.Plugin; - import org.bukkit.util.BoundingBox; - import org.bukkit.util.NumberConversions; - import org.bukkit.util.Vector; -+import org.galemc.gale.executor.queue.ScheduledServerThreadTaskQueues; - - import net.md_5.bungee.api.chat.BaseComponent; // Spigot - -@@ -1318,7 +1319,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { - for (net.minecraft.world.level.chunk.ChunkAccess chunk : list) { - chunkProviderServer.addTicketAtLevel(net.minecraft.server.level.TicketType.POST_TELEPORT, chunk.getPos(), 33, CraftEntity.this.getEntityId()); - } -- net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> { -+ ScheduledServerThreadTaskQueues.add(() -> { // Gale - base thread pool - try { - ret.complete(CraftEntity.this.teleport(locationClone, cause) ? Boolean.TRUE : Boolean.FALSE); - } catch (Throwable throwable) { -@@ -1328,7 +1329,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { - net.minecraft.server.MinecraftServer.LOGGER.error("Failed to teleport entity " + CraftEntity.this, throwable); - ret.completeExceptionally(throwable); - } -- }); -+ }, ScheduledServerThreadTaskQueues.TELEPORT_ASYNC_TASK_MAX_DELAY); // Gale - base thread pool - }); - - return ret; -diff --git a/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java b/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java -index 9571aae593999d11b3908856b0295a7d6b588007..ed2841d3a6c6d90ad02266f38c0821bca4f549f1 100644 ---- a/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java -+++ b/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java -@@ -264,7 +264,7 @@ public class GaleConfigurations extends Configurations -+ *
  • Default: -1
  • -+ *
  • Vanilla: -1
  • -+ * -+ */ -+ @Setting("default") -+ public int defaultValue = -1; -+ -+ /** -+ * The default maximum delay for completing a {@link java.util.concurrent.CompletableFuture} -+ * for a chunk load, after the chunk has already finished loading. -+ * Given in ticks. -+ * Any value < 0 uses {@link #defaultValue}. -+ *
      -+ *
    • Default: 0
    • -+ *
    • Vanilla: -1
    • -+ *
    -+ */ -+ public int completeChunkFuture = 0; -+ -+ /** -+ * The default maximum delay for completing the steps needed to take when a player is joining and the -+ * necessary chunk has been loaded. -+ * Given in ticks. -+ * Any value < 0 uses {@link #defaultValue}. -+ *
      -+ *
    • Default: 19
    • -+ *
    • Vanilla: -1
    • -+ *
    -+ */ -+ public int postChunkLoadJoin = 19; -+ -+ /** -+ * The default maximum delay for chunk packets to be modified for anti-xray. -+ * Given in ticks. -+ * Any value < 0 uses {@link #defaultValue}. -+ *
      -+ *
    • Default: 19
    • -+ *
    • Vanilla: -1
    • -+ *
    -+ */ -+ public int antiXrayModifyBlocks = 19; -+ -+ /** -+ * The default maximum delay for entities to be teleported when a teleport is started asynchronously. -+ * Given in ticks. -+ * Any value < 0 uses {@link #defaultValue}. -+ *
      -+ *
    • Default: -1
    • -+ *
    • Vanilla: -1
    • -+ *
    -+ */ -+ public int teleportAsync = -1; -+ -+ /** -+ * The default maximum delay for command completion suggestions to be sent to the player. -+ * Any value < 0 uses {@link #defaultValue}. -+ *
      -+ *
    • Default: 9
    • -+ *
    • Vanilla: -1
    • -+ *
    -+ */ -+ public int sendCommandCompletionSuggestions = 9; -+ -+ /** -+ * The default maximum delay for players to get kicked for command packet spam. -+ * Given in ticks. -+ * Any value < 0 uses {@link #defaultValue}. -+ *
      -+ *
    • Default: 0
    • -+ *
    • Vanilla: -1
    • -+ *
    -+ */ -+ public int kickForCommandPacketSpam = 0; -+ -+ /** -+ * The default maximum delay for players to get kicked for place-recipe packet spam. -+ * Given in ticks. -+ * Any value < 0 uses {@link #defaultValue}. -+ *
      -+ *
    • Default: 0
    • -+ *
    • Vanilla: -1
    • -+ *
    -+ */ -+ public int kickForRecipePacketSpam = 0; -+ -+ /** -+ * The default maximum delay for players to get kicked for sending invalid packets trying to -+ * send book content that is too large, which usually indicates they are attempting to abuse an exploit. -+ * Given in ticks. -+ * Any value < 0 uses {@link #defaultValue}. -+ *
      -+ *
    • Default: -1
    • -+ *
    • Vanilla: -1
    • -+ *
    -+ */ -+ public int kickForBookTooLargePacket = -1; -+ -+ /** -+ * The default maximum delay for players to get kicked for editing a book too quickly. -+ * Given in ticks. -+ * Any value < 0 uses {@link #defaultValue}. -+ *
      -+ *
    • Default: -1
    • -+ *
    • Vanilla: -1
    • -+ *
    -+ */ -+ public int kickForEditingBookTooQuickly = -1; -+ -+ /** -+ * The default maximum delay for players to get kicked for sending a chat packet with illegal characters. -+ * Given in ticks. -+ * Any value < 0 uses {@link #defaultValue}. -+ *
      -+ *
    • Default: -1
    • -+ *
    • Vanilla: -1
    • -+ *
    -+ */ -+ public int kickForIllegalCharactersInChatPacket = -1; -+ -+ /** -+ * The default maximum delay for players to get kicked for sending an out-of-order chat packet. -+ * Given in ticks. -+ * Any value < 0 uses {@link #defaultValue}. -+ *
      -+ *
    • Default: -1
    • -+ *
    • Vanilla: -1
    • -+ *
    -+ */ -+ public int kickForOutOfOrderChatPacket = -1; -+ -+ /** -+ * The default maximum delay for handling player disconnects. -+ * Any value < 0 uses {@link #defaultValue}. -+ *
      -+ *
    • Default: -1
    • -+ *
    • Vanilla: -1
    • -+ *
    -+ */ -+ public int handleDisconnect = -1; -+ -+ @Override -+ public void postProcess() { -+ while (!ScheduledServerThreadTaskQueues.writeLock.tryLock()); -+ try { -+ // Update the values in MinecraftServerBlockableEventLoop for quick access -+ ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY = this.defaultValue >= 0 ? this.defaultValue : 2; -+ ScheduledServerThreadTaskQueues.COMPLETE_CHUNK_FUTURE_TASK_MAX_DELAY = this.completeChunkFuture >= 0 ? this.completeChunkFuture : ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; -+ ScheduledServerThreadTaskQueues.POST_CHUNK_LOAD_JOIN_TASK_MAX_DELAY = this.postChunkLoadJoin >= 0 ? this.postChunkLoadJoin : ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; -+ ScheduledServerThreadTaskQueues.ANTI_XRAY_MODIFY_BLOCKS_TASK_MAX_DELAY = this.antiXrayModifyBlocks >= 0 ? this.antiXrayModifyBlocks : ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; -+ ScheduledServerThreadTaskQueues.TELEPORT_ASYNC_TASK_MAX_DELAY = this.teleportAsync >= 0 ? this.teleportAsync : ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; -+ ScheduledServerThreadTaskQueues.SEND_COMMAND_COMPLETION_SUGGESTIONS_TASK_MAX_DELAY = this.sendCommandCompletionSuggestions >= 0 ? this.sendCommandCompletionSuggestions : ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; -+ ScheduledServerThreadTaskQueues.KICK_FOR_COMMAND_PACKET_SPAM_TASK_MAX_DELAY = this.kickForCommandPacketSpam >= 0 ? this.kickForCommandPacketSpam : ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; -+ ScheduledServerThreadTaskQueues.KICK_FOR_RECIPE_PACKET_SPAM_TASK_MAX_DELAY = this.kickForRecipePacketSpam >= 0 ? this.kickForRecipePacketSpam : ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; -+ ScheduledServerThreadTaskQueues.KICK_FOR_BOOK_TOO_LARGE_PACKET_TASK_MAX_DELAY = this.kickForBookTooLargePacket >= 0 ? this.kickForBookTooLargePacket : ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; -+ ScheduledServerThreadTaskQueues.KICK_FOR_EDITING_BOOK_TOO_QUICKLY_TASK_MAX_DELAY = this.kickForEditingBookTooQuickly >= 0 ? this.kickForEditingBookTooQuickly : ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; -+ ScheduledServerThreadTaskQueues.KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY = this.kickForIllegalCharactersInChatPacket >= 0 ? this.kickForIllegalCharactersInChatPacket : ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; -+ ScheduledServerThreadTaskQueues.KICK_FOR_OUT_OF_ORDER_CHAT_PACKET_TASK_MAX_DELAY = this.kickForOutOfOrderChatPacket >= 0 ? this.kickForOutOfOrderChatPacket : ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; -+ ScheduledServerThreadTaskQueues.HANDLE_DISCONNECT_TASK_MAX_DELAY = this.handleDisconnect >= 0 ? this.handleDisconnect : ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; -+ // Change the length of the pendingRunnables array of queues -+ int maxDelay = 0; -+ for (int delay : new int[]{ -+ ScheduledServerThreadTaskQueues.DEFAULT_TASK_MAX_DELAY, -+ ScheduledServerThreadTaskQueues.COMPLETE_CHUNK_FUTURE_TASK_MAX_DELAY, -+ ScheduledServerThreadTaskQueues.POST_CHUNK_LOAD_JOIN_TASK_MAX_DELAY, -+ ScheduledServerThreadTaskQueues.ANTI_XRAY_MODIFY_BLOCKS_TASK_MAX_DELAY, -+ ScheduledServerThreadTaskQueues.TELEPORT_ASYNC_TASK_MAX_DELAY, -+ ScheduledServerThreadTaskQueues.SEND_COMMAND_COMPLETION_SUGGESTIONS_TASK_MAX_DELAY, -+ ScheduledServerThreadTaskQueues.KICK_FOR_COMMAND_PACKET_SPAM_TASK_MAX_DELAY, -+ ScheduledServerThreadTaskQueues.KICK_FOR_RECIPE_PACKET_SPAM_TASK_MAX_DELAY, -+ ScheduledServerThreadTaskQueues.KICK_FOR_BOOK_TOO_LARGE_PACKET_TASK_MAX_DELAY, -+ ScheduledServerThreadTaskQueues.KICK_FOR_EDITING_BOOK_TOO_QUICKLY_TASK_MAX_DELAY, -+ ScheduledServerThreadTaskQueues.KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY, -+ ScheduledServerThreadTaskQueues.KICK_FOR_OUT_OF_ORDER_CHAT_PACKET_TASK_MAX_DELAY, -+ ScheduledServerThreadTaskQueues.HANDLE_DISCONNECT_TASK_MAX_DELAY -+ }) { -+ if (delay > maxDelay) { -+ maxDelay = delay; -+ } -+ } -+ int newPendingRunnablesLength = maxDelay + 1; -+ int oldPendingRunnablesLength = ScheduledServerThreadTaskQueues.queues.length; -+ if (oldPendingRunnablesLength != newPendingRunnablesLength) { -+ if (oldPendingRunnablesLength > newPendingRunnablesLength) { -+ // Move all tasks in queues that will be removed to the last queue -+ for (int i = newPendingRunnablesLength + 1; i < ScheduledServerThreadTaskQueues.queues.length; i++) { -+ ScheduledServerThreadTaskQueues.queues[maxDelay].addAll(ScheduledServerThreadTaskQueues.queues[i]); -+ } -+ // Update the first queue with elements index -+ if (ScheduledServerThreadTaskQueues.firstQueueWithPotentialTasksIndex >= newPendingRunnablesLength) { -+ ScheduledServerThreadTaskQueues.firstQueueWithPotentialTasksIndex = maxDelay; -+ } -+ } -+ ScheduledServerThreadTaskQueues.queues = Arrays.copyOf(ScheduledServerThreadTaskQueues.queues, newPendingRunnablesLength); -+ if (newPendingRunnablesLength > oldPendingRunnablesLength) { -+ // Create new queues -+ for (int i = oldPendingRunnablesLength; i < newPendingRunnablesLength; i++) { -+ ScheduledServerThreadTaskQueues.queues[i] = new MultiThreadedQueue<>(); -+ } -+ } -+ } -+ } finally { -+ ScheduledServerThreadTaskQueues.writeLock.unlock(); -+ } -+ } -+ -+ } -+ // Gale end - base thread pool -+ - } - - public GameplayMechanics gameplayMechanics; -diff --git a/src/main/java/org/galemc/gale/executor/AbstractBlockableEventLoop.java b/src/main/java/org/galemc/gale/executor/AbstractBlockableEventLoop.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e9d778a078bee6b6f1c21078c445b48fc276e985 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/AbstractBlockableEventLoop.java -@@ -0,0 +1,21 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor; -+ -+import net.minecraft.util.thread.BlockableEventLoop; -+ -+import java.util.concurrent.Executor; -+ -+/** -+ * An interface for the common functionality of {@link BlockableEventLoop} and {@link MinecraftServerBlockableEventLoop}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public interface AbstractBlockableEventLoop extends Executor { -+ -+ boolean isSameThread(); -+ -+ @SuppressWarnings("unused") -+ void executeIfPossible(Runnable runnable); -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/MinecraftServerBlockableEventLoop.java b/src/main/java/org/galemc/gale/executor/MinecraftServerBlockableEventLoop.java -new file mode 100644 -index 0000000000000000000000000000000000000000..2da7a1a787bed2e03039796d56201870548ad0e4 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/MinecraftServerBlockableEventLoop.java -@@ -0,0 +1,187 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor; -+ -+import com.mojang.logging.LogUtils; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.util.thread.BlockableEventLoop; -+import net.minecraft.util.thread.ProcessorHandle; -+import net.minecraft.util.thread.ReentrantBlockableEventLoop; -+import org.galemc.gale.executor.annotation.thread.ServerThreadOnly; -+import org.galemc.gale.executor.queue.BaseTaskQueues; -+import org.galemc.gale.executor.queue.ScheduledServerThreadTaskQueues; -+import org.galemc.gale.executor.thread.ServerThread; -+import org.jetbrains.annotations.NotNull; -+import org.slf4j.Logger; -+ -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.RejectedExecutionException; -+import java.util.function.BooleanSupplier; -+import java.util.function.Supplier; -+ -+/** -+ * This is a base class for {@link MinecraftServer}, as a replacement of {@link BlockableEventLoop} -+ * (and the intermediary class {@link ReentrantBlockableEventLoop}). -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public class MinecraftServerBlockableEventLoop implements ProcessorHandle, AbstractBlockableEventLoop { -+ -+ private static final String NAME = "Server"; -+ private static final Logger LOGGER = LogUtils.getLogger(); -+ -+ public static volatile int blockingCount; -+ private static volatile int reentrantCount; -+ -+ public static boolean scheduleExecutables() { -+ return (reentrantCount != 0 || Thread.currentThread() != ServerThread.getInstance()) && !MinecraftServer.SERVER.isStopped(); -+ } -+ -+ protected boolean runningTask() { -+ return reentrantCount != 0; -+ } -+ -+ public CompletableFuture submit(Supplier task) { -+ return scheduleExecutables() ? CompletableFuture.supplyAsync(task, this) : CompletableFuture.completedFuture(task.get()); -+ } -+ -+ private CompletableFuture submitAsync(Runnable runnable) { -+ return CompletableFuture.supplyAsync(() -> { -+ runnable.run(); -+ return null; -+ }, this); -+ } -+ -+ public CompletableFuture submit(Runnable task) { -+ if (scheduleExecutables()) { -+ return this.submitAsync(task); -+ } else { -+ task.run(); -+ return CompletableFuture.completedFuture(null); -+ } -+ } -+ -+ public void executeBlocking(Runnable runnable) { -+ if (Thread.currentThread() != ServerThread.getInstance()) { -+ this.submitAsync(runnable).join(); -+ } else { -+ runnable.run(); -+ } -+ } -+ -+ /** -+ * @deprecated Use {@link ScheduledServerThreadTaskQueues#add(Runnable, int)} instead: -+ * do not rely on {@link ScheduledServerThreadTaskQueues#DEFAULT_TASK_MAX_DELAY}. -+ */ -+ @Deprecated -+ @Override -+ public void tell(@NotNull Runnable message) { -+ ScheduledServerThreadTaskQueues.add(() -> { -+ //noinspection NonAtomicOperationOnVolatileField -+ ++reentrantCount; -+ try { -+ message.run(); -+ } catch (Exception var3) { -+ if (var3.getCause() instanceof ThreadDeath) throw var3; // Paper -+ LOGGER.error(LogUtils.FATAL_MARKER, "Error executing task on {}", NAME, var3); -+ } finally { -+ //noinspection NonAtomicOperationOnVolatileField -+ --reentrantCount; -+ if (MinecraftServer.isWaitingUntilNextTick) { -+ MinecraftServer.signalServerThreadIfCurrentManagedBlockStopConditionBecameTrue(); -+ } -+ } -+ MinecraftServer.SERVER.executeMidTickTasks(); // Paper - execute chunk tasks mid tick -+ }); -+ } -+ -+ @Override -+ public void execute(@NotNull Runnable var1) { -+ if (scheduleExecutables()) { -+ this.tell(var1); -+ } else { -+ var1.run(); -+ } -+ } -+ -+ @Override -+ public boolean isSameThread() { -+ return Thread.currentThread() == MinecraftServer.serverThread; -+ } -+ -+ @Override -+ public void executeIfPossible(Runnable runnable) { -+ if (MinecraftServer.SERVER.isStopped()) { -+ throw new RejectedExecutionException("Server already shutting down"); -+ } else { -+ this.execute(runnable); -+ } -+ } -+ -+ /** -+ * Runs all tasks, regardless of which tick they must be finished in, or whether there is time. -+ */ -+ @ServerThreadOnly -+ protected void runAllMainThreadTasksForAllTicks() { -+ Runnable task; -+ while (true) { -+ // Force polling every tasks regardless of the tick they have to be finished by -+ MinecraftServer.isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking = false; -+ task = ScheduledServerThreadTaskQueues.poll(ServerThread.getInstance(), true); -+ if (task == null) { -+ break; -+ } -+ task.run(); -+ } -+ } -+ -+ /** -+ * Runs all tasks while there is time. -+ * Runs at least all tasks that must be finished in the current tick, regardless of whether there is time. -+ */ -+ @ServerThreadOnly -+ protected void runAllTasksWithinTimeOrForCurrentTick() { -+ Runnable task; -+ while (true) { -+ /* -+ Update this value accurately: we are in 'spare time' here, we may have more time or not, and we are -+ definitely not already blocking. -+ */ -+ MinecraftServer.isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking = !MinecraftServer.SERVER.haveTime(); -+ task = BaseTaskQueues.anyTickScheduledServerThread.poll(ServerThread.getInstance()); -+ if (task == null) { -+ break; -+ } -+ task.run(); -+ } -+ } -+ -+ @ServerThreadOnly -+ public void managedBlock(@NotNull BooleanSupplier stopCondition) { -+ MinecraftServer.currentManagedBlockStopCondition = stopCondition; -+ try { -+ // Check stop condition beforehand to prevent unnecessarily releasing main thread -+ MinecraftServer.currentManagedBlockStopConditionHasBecomeTrue = false; -+ if (stopCondition.getAsBoolean()) { -+ MinecraftServer.currentManagedBlockStopConditionHasBecomeTrue = true; -+ return; -+ } -+ //noinspection NonAtomicOperationOnVolatileField -+ ++blockingCount; -+ try { -+ MinecraftServer.serverThread.runTasksUntil(null, stopCondition, null); -+ } finally { -+ //noinspection NonAtomicOperationOnVolatileField -+ --blockingCount; -+ } -+ } finally { -+ MinecraftServer.currentManagedBlockStopCondition = null; -+ } -+ } -+ -+ @Override -+ public @NotNull String name() { -+ return NAME; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/TaskSpan.java b/src/main/java/org/galemc/gale/executor/TaskSpan.java -new file mode 100644 -index 0000000000000000000000000000000000000000..99dcb582bf6557c54d9e1477434710ba92b2d87a ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/TaskSpan.java -@@ -0,0 +1,70 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor; -+ -+import java.util.Arrays; -+ -+/** -+ * An enum for the behaviour of a task, in terms of its potential to yield -+ * and its expected time cost to finish. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public enum TaskSpan { -+ -+ /** -+ * Indicates that a task that may potentially yield. -+ * The expected duration of this task is not specifically established. -+ */ -+ YIELDING(true), -+ /** -+ * Indicates that a task is yield-free. -+ * The expected duration of this task is not specifically established. -+ */ -+ FREE(false), -+ /** -+ * Indicates that a task is yield-free, -+ * and has an expected running time that is below double the approximate time cost of two context switches. -+ * It is assumed that a context switch takes approximately 5 microseconds, and as such, -+ * this span indicates that a task has an expected running time of 20 or fewer microseconds. -+ */ -+ TINY(false); -+ -+ /** -+ * Equal to {@link #ordinal()}. -+ */ -+ public final int ordinal; -+ -+ /** -+ * Whether tasks with this span are potentially yielding. -+ */ -+ public final boolean isYielding; -+ -+ /** -+ * Equal to the negation of {@link #isYielding}. -+ */ -+ public final boolean isNotYielding; -+ -+ TaskSpan(boolean isYielding) { -+ this.ordinal = this.ordinal(); -+ this.isYielding = isYielding; -+ this.isNotYielding = !this.isYielding; -+ } -+ -+ /** -+ * Equal to {@link #values()}. -+ */ -+ public static final TaskSpan[] VALUES = values(); -+ -+ /** -+ * Equal to {@link #VALUES}{@code .length}. -+ */ -+ public static final int length = VALUES.length; -+ -+ /** -+ * Equal to {@link #VALUES} for which {@link #isNotYielding} is true. -+ */ -+ public static final TaskSpan[] NON_YIELDING_VALUES = Arrays.stream(VALUES).filter(span -> span.isNotYielding).toList().toArray(new TaskSpan[length - 1]); -+ -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java -index a4dc0ebe48fdd352387f06be42ff46fc11ee5822..d324c303245bcbedaaaab573803d73caff941901 100644 ---- a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java -+++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java -@@ -2,6 +2,8 @@ - - package org.galemc.gale.executor.annotation; - -+import org.galemc.gale.executor.thread.AbstractYieldingThread; -+ - import java.lang.annotation.Documented; - import java.lang.annotation.ElementType; - import java.lang.annotation.Target; -@@ -15,6 +17,9 @@ import java.lang.annotation.Target; - *
    - * In a method annotated with {@link PotentiallyBlocking}, fields and methods annotated with - * {@link PotentiallyBlocking}, {@link PotentiallyYielding} or {@link YieldFree} may all be used. -+ *
    -+ * Methods that are potentially blocking, including those annotated with {@link PotentiallyBlocking}, must never -+ * be called on an {@link AbstractYieldingThread}. - * - * @author Martijn Muijsers under AGPL-3.0 - */ -diff --git a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java -index 44b70d68ba6823ab72ea9af4b7774051785c0a2b..7ff4e4ab43d316e319efb33b2dd365d679a58118 100644 ---- a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java -+++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java -@@ -2,13 +2,16 @@ - - package org.galemc.gale.executor.annotation; - -+import org.galemc.gale.executor.lock.YieldingLock; -+import org.galemc.gale.executor.thread.AbstractYieldingThread; -+ - import java.lang.annotation.Documented; - import java.lang.annotation.ElementType; - import java.lang.annotation.Target; - - /** - * An annotation primarily for methods, identifying methods that do not block, but may yield to other tasks -- * under certain circumstances. -+ * under certain circumstances, such as when attempting to acquire a {@link YieldingLock}. - *
    - * When applied to a class, this annotation indicates it holds for all methods, both instance and static, - * belonging to the class, or any superclass thereof, or any inner or statically nested class of the class or -@@ -16,6 +19,9 @@ import java.lang.annotation.Target; - *
    - * In a method annotated with {@link PotentiallyYielding}, the only methods that can be called are those - * annotated with {@link PotentiallyYielding} or {@link YieldFree}. -+ *
    -+ * It should be assumed that any method annotated with {@link PotentiallyYielding} is potentially blocking if used -+ * on a thread that is not a {@link AbstractYieldingThread}. - * - * @author Martijn Muijsers under AGPL-3.0 - */ -diff --git a/src/main/java/org/galemc/gale/executor/annotation/thread/AssistThreadOnly.java b/src/main/java/org/galemc/gale/executor/annotation/thread/AssistThreadOnly.java -new file mode 100644 -index 0000000000000000000000000000000000000000..203799c5a9ddec3e665a7476f5e48a2c0f457b04 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/annotation/thread/AssistThreadOnly.java -@@ -0,0 +1,37 @@ -+// Gale - thread-safety annotations -+ -+package org.galemc.gale.executor.annotation.thread; -+ -+import org.galemc.gale.executor.annotation.Access; -+import org.galemc.gale.executor.annotation.PotentiallyBlocking; -+import org.galemc.gale.executor.thread.AssistThread; -+ -+import java.lang.annotation.Documented; -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Target; -+ -+/** -+ * An annotation primarily for methods, identifying methods that can only be called on a thread that is an instance -+ * of {@link AssistThread}. -+ *
    -+ * This annotation can also be used on fields or classes, similar to {@link ThreadRestricted}. -+ *
    -+ * In a method annotated with {@link AssistThreadOnly}, fields and methods annotated with -+ * {@link AssistThreadOnly}, {@link BaseThreadOnly} or {@link AnyThreadSafe} may be used. -+ *
    -+ * Methods that are annotated with {@link AssistThreadOnly} must never call methods that are annotated with -+ * {@link PotentiallyBlocking}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@SuppressWarnings("unused") -+@Documented -+@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD}) -+public @interface AssistThreadOnly { -+ -+ /** -+ * @see ThreadRestricted#fieldAccess() -+ */ -+ Access value() default Access.READ_WRITE; -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/annotation/thread/BaseThreadOnly.java b/src/main/java/org/galemc/gale/executor/annotation/thread/BaseThreadOnly.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e682953181dbf208e731ab5b081664a129210310 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/annotation/thread/BaseThreadOnly.java -@@ -0,0 +1,36 @@ -+// Gale - thread-safety annotations -+ -+package org.galemc.gale.executor.annotation.thread; -+ -+import org.galemc.gale.executor.annotation.Access; -+import org.galemc.gale.executor.annotation.PotentiallyBlocking; -+import org.galemc.gale.executor.thread.BaseThread; -+ -+import java.lang.annotation.Documented; -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Target; -+ -+/** -+ * An annotation primarily for methods, identifying methods that can only be called on a thread that is an instance -+ * of {@link BaseThread}. -+ *
    -+ * This annotation can also be used on fields or classes, similar to {@link ThreadRestricted}. -+ *
    -+ * In a method annotated with {@link BaseThreadOnly}, fields and methods annotated with -+ * {@link BaseThreadOnly} or {@link AnyThreadSafe} may be used. -+ *
    -+ * Methods that are annotated with {@link BaseThreadOnly} must never call methods that are annotated with -+ * {@link PotentiallyBlocking}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@Documented -+@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD}) -+public @interface BaseThreadOnly { -+ -+ /** -+ * @see ThreadRestricted#fieldAccess() -+ */ -+ Access value() default Access.READ_WRITE; -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/annotation/thread/OriginalServerThreadOnly.java b/src/main/java/org/galemc/gale/executor/annotation/thread/OriginalServerThreadOnly.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d6476e007de11fb4e556cee6ec6eea107dc814fa ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/annotation/thread/OriginalServerThreadOnly.java -@@ -0,0 +1,39 @@ -+// Gale - thread-safety annotations -+ -+package org.galemc.gale.executor.annotation.thread; -+ -+import org.galemc.gale.executor.annotation.Access; -+import org.galemc.gale.executor.annotation.PotentiallyBlocking; -+import org.galemc.gale.executor.thread.OriginalServerThread; -+ -+import java.lang.annotation.Documented; -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Target; -+ -+/** -+ * An annotation primarily for methods, identifying methods that can only be called from the -+ * {@link OriginalServerThread}. -+ *
    -+ * This annotation can also be used on fields, similar to {@link ThreadRestricted}. -+ *
    -+ * In a method annotated with {@link OriginalServerThreadOnly}, fields and methods annotated with -+ * {@link OriginalServerThreadOnly}, {@link ServerThreadOnly}, {@link BaseThreadOnly} -+ * or {@link AnyThreadSafe} may be used. -+ *
    -+ * Methods that are annotated with {@link OriginalServerThreadOnly} must never call methods that are annotated with -+ * {@link PotentiallyBlocking}. -+ * -+ * @see ThreadRestricted -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@Documented -+@Target({ElementType.METHOD, ElementType.FIELD}) -+public @interface OriginalServerThreadOnly { -+ -+ /** -+ * @see ThreadRestricted#fieldAccess() -+ */ -+ Access value() default Access.READ_WRITE; -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/annotation/thread/ServerThreadOnly.java b/src/main/java/org/galemc/gale/executor/annotation/thread/ServerThreadOnly.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d27ee27a65635d0136c5c9e33925b64036ae5cd3 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/annotation/thread/ServerThreadOnly.java -@@ -0,0 +1,37 @@ -+// Gale - thread-safety annotations -+ -+package org.galemc.gale.executor.annotation.thread; -+ -+import org.galemc.gale.executor.annotation.Access; -+import org.galemc.gale.executor.annotation.PotentiallyBlocking; -+import org.galemc.gale.executor.thread.ServerThread; -+ -+import java.lang.annotation.Documented; -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Target; -+ -+/** -+ * An annotation primarily for methods, identifying methods that can only be called from a {@link ServerThread}. -+ *
    -+ * This annotation can also be used on fields or classes, similar to {@link ThreadRestricted}. -+ *
    -+ * In a method annotated with {@link ServerThreadOnly}, fields and methods annotated with -+ * {@link ServerThreadOnly}, {@link BaseThreadOnly} or {@link AnyThreadSafe} may be used. -+ *
    -+ * Methods that are annotated with {@link ServerThreadOnly} must never call methods that are annotated with -+ * {@link PotentiallyBlocking}. -+ * -+ * @see ThreadRestricted -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@Documented -+@Target({ElementType.METHOD, ElementType.FIELD}) -+public @interface ServerThreadOnly { -+ -+ /** -+ * @see ThreadRestricted#fieldAccess() -+ */ -+ Access value() default Access.READ_WRITE; -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/lock/MultipleWaitingBaseThreadsYieldingLock.java b/src/main/java/org/galemc/gale/executor/lock/MultipleWaitingBaseThreadsYieldingLock.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a248a9ea644a8bb4175da2e1903483ab6866bc48 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/lock/MultipleWaitingBaseThreadsYieldingLock.java -@@ -0,0 +1,39 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.lock; -+ -+import org.galemc.gale.executor.thread.BaseThread; -+ -+import java.util.concurrent.atomic.AtomicInteger; -+import java.util.concurrent.locks.Lock; -+ -+/** -+ * A {@link YieldingLock} for which multiple {@link BaseThread}s may be waiting at the same time. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@SuppressWarnings("unused") -+public class MultipleWaitingBaseThreadsYieldingLock extends YieldingLock { -+ -+ private final AtomicInteger waitingThreadCount = new AtomicInteger(); -+ -+ public MultipleWaitingBaseThreadsYieldingLock(Lock innerLock) { -+ super(innerLock); -+ } -+ -+ @Override -+ public void incrementWaitingThreads() { -+ this.waitingThreadCount.incrementAndGet(); -+ } -+ -+ @Override -+ public void decrementWaitingThreads() { -+ this.waitingThreadCount.decrementAndGet(); -+ } -+ -+ @Override -+ protected boolean hasWaitingThreads() { -+ return this.waitingThreadCount.get() > 0; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/lock/SingleWaitingBaseThreadYieldingLock.java b/src/main/java/org/galemc/gale/executor/lock/SingleWaitingBaseThreadYieldingLock.java -new file mode 100644 -index 0000000000000000000000000000000000000000..686e16da8372085196d8f92adb881f82dd5c2947 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/lock/SingleWaitingBaseThreadYieldingLock.java -@@ -0,0 +1,39 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.lock; -+ -+import org.galemc.gale.executor.thread.BaseThread; -+ -+import java.util.concurrent.atomic.AtomicInteger; -+import java.util.concurrent.locks.Lock; -+ -+/** -+ * A {@link YieldingLock} for which one {@link BaseThread} may be waiting at any time. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@SuppressWarnings("unused") -+public class SingleWaitingBaseThreadYieldingLock extends YieldingLock { -+ -+ private volatile boolean hasWaitingThread = false; -+ -+ public SingleWaitingBaseThreadYieldingLock(Lock innerLock) { -+ super(innerLock); -+ } -+ -+ @Override -+ public void incrementWaitingThreads() { -+ hasWaitingThread = true; -+ } -+ -+ @Override -+ public void decrementWaitingThreads() { -+ hasWaitingThread = false; -+ } -+ -+ @Override -+ protected boolean hasWaitingThreads() { -+ return this.hasWaitingThread; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/lock/YieldingLock.java b/src/main/java/org/galemc/gale/executor/lock/YieldingLock.java -new file mode 100644 -index 0000000000000000000000000000000000000000..44b8bd5fd9a3ee2e484c81104523ba956f8d982f ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/lock/YieldingLock.java -@@ -0,0 +1,213 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.lock; -+ -+import org.galemc.gale.concurrent.CheckableLock; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.PotentiallyYielding; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.thread.AbstractYieldingThread; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.pool.BaseThreadActivation; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.concurrent.TimeUnit; -+import java.util.concurrent.locks.Condition; -+import java.util.concurrent.locks.Lock; -+import java.util.concurrent.locks.ReentrantLock; -+ -+/** -+ * A wrapper for a lock that can be acquired, but if not able to be acquired right away, can cause the current thread -+ * to perform other tasks, attempting to acquire the lock again at a later time. -+ *
    -+ * The lock is reentrant if the underlying controlled lock is. -+ *
    -+ * The lock only be speculatively acquired from any {@link AbstractYieldingThread}. -+ * Acquiring it on a thread that is not an {@link AbstractYieldingThread} will perform regular locking -+ * on the underlying controlled lock, which typically blocks the thread. -+ *
    -+ * A thread cannot acquire a {@link YieldingLock} when it already owns one. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+public abstract class YieldingLock implements CheckableLock { -+ -+ private final Lock innerLock; -+ -+ /** -+ * The same value as {@link #lock}, or null if it is not an instance of {@link CheckableLock}. -+ */ -+ private final @Nullable CheckableLock innerCheckableLock; -+ -+ /** -+ * The same value as {@link #lock}, or null if it is not an instance of {@link ReentrantLock}. -+ */ -+ private final @Nullable ReentrantLock innerReentrantLock; -+ -+ /** -+ * Whether the {@link #innerLock} can be truly 'held' as lock. -+ * Can be set to false via {@link #withCanNotBeHeld()} for locks that are not real, but -+ * merely use the {@link Lock} interface allow base threads to wait for them. -+ */ -+ private boolean canBeHeld; -+ -+ /** -+ * Whether any threads can be {@linkplain BaseThread#signal signalled} for this lock being unlocked. -+ * This is set to false when a thread is signalled, until the thread has actually attempted to -+ * acquire the lock, so that we do not activate multiple threads for the same unlocking event. -+ */ -+ public volatile boolean canBeSignalledFor = true; -+ -+ public YieldingLock(Lock innerLock) { -+ this.innerLock = innerLock; -+ if (innerLock instanceof CheckableLock checkableLock) { -+ this.innerCheckableLock = checkableLock; -+ this.innerReentrantLock = null; -+ } else if (innerLock instanceof ReentrantLock reentrantLock) { -+ this.innerCheckableLock = null; -+ this.innerReentrantLock = reentrantLock; -+ } else { -+ throw new IllegalArgumentException("The innerLock passed to the YieldingLock() constructor must be an instance of CheckableLock or ReentrantLock"); -+ } -+ } -+ -+ /** -+ * Attempts to acquire the lock immediately. -+ * -+ * @return Whether the lock was acquired. -+ */ -+ @YieldFree -+ @Override -+ public boolean tryLock() { -+ if (innerLock.tryLock()) { -+ // Increment the YieldingLock count of the current thread -+ if (this.canBeHeld) { -+ @Nullable AbstractYieldingThread yieldingThread = AbstractYieldingThread.currentYieldingThread(); -+ if (yieldingThread != null) { -+ yieldingThread.incrementHeldYieldingLockCount(); -+ } -+ } -+ return true; -+ } -+ return false; -+ } -+ -+ /** -+ * Acquires the lock. -+ *
    -+ * If the current threads is an {@link AbstractYieldingThread}, -+ * this will yield to other tasks while the lock can not be acquired. -+ * Otherwise, this will block until the lock is acquired. -+ */ -+ @PotentiallyYielding -+ @Override -+ public void lock() { -+ // Find out our current yielding thread, if any -+ @Nullable AbstractYieldingThread yieldingThread = AbstractYieldingThread.currentYieldingThread(); -+ // Try to acquire the lock straight away -+ if (!this.innerLock.tryLock()) { -+ // If we are not on a yielding thread, we wait for the lock instead of yielding -+ if (yieldingThread == null) { -+ this.innerLock.lock(); -+ return; -+ } -+ // Otherwise, we yield to other tasks until the lock can be acquired -+ yieldingThread.yieldUntil(null, null, this); -+ } -+ // Increment the YieldingLock count of the current thread -+ if (this.canBeHeld) { -+ if (yieldingThread != null) { -+ yieldingThread.incrementHeldYieldingLockCount(); -+ } -+ } -+ } -+ -+ /** -+ * Releases the lock (must be called after having completed the computation block that required the lock). -+ */ -+ @Override -+ public void unlock() { -+ this.innerLock.unlock(); -+ // Decrement the YieldingLock count of the current thread -+ if (this.canBeHeld) { -+ @Nullable AbstractYieldingThread yieldingThread = AbstractYieldingThread.currentYieldingThread(); -+ if (yieldingThread != null) { -+ yieldingThread.decrementHeldYieldingLockCount(); -+ } -+ } -+ /* -+ Potentially signal a thread that this lock has become available. -+ Another thread could also acquire the lock at this moment, so when we signal the thread we obtain below, -+ it may already be too late for the polled thread to acquire this lock -+ (but note that the same thread cannot have been added again because only the thread itself can do that - -+ and it is still waiting). -+ Also note that this lock may still be locked (since it may be reentrant) so we check the locked state. -+ */ -+ if (!this.isLocked() && this.hasWaitingThreads()) { -+ BaseThreadActivation.yieldingLockWithWaitingThreadsWasUnlocked(); -+ } -+ } -+ -+ @SuppressWarnings("RedundantThrows") -+ @Override -+ public boolean tryLock(long l, @NotNull TimeUnit timeUnit) throws InterruptedException { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @SuppressWarnings("RedundantThrows") -+ @Override -+ public void lockInterruptibly() throws InterruptedException { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @NotNull -+ @Override -+ public Condition newCondition() { -+ // The inner lock may itself not support newCondition and throw UnsupportedOperationException -+ return this.innerLock.newCondition(); -+ } -+ -+ @Override -+ public boolean isLocked() { -+ //noinspection DataFlowIssue -+ return this.innerCheckableLock != null ? this.innerCheckableLock.isLocked() : this.innerReentrantLock.isLocked(); -+ } -+ -+ @Override -+ public boolean isHeldByCurrentThread() { -+ return this.innerCheckableLock != null ? this.innerCheckableLock.isHeldByCurrentThread() : this.innerReentrantLock.isHeldByCurrentThread(); -+ } -+ -+ /** -+ * Increments the number of threads waiting for this lock to be released. -+ */ -+ @YieldFree -+ public abstract void incrementWaitingThreads(); -+ -+ /** -+ * Decrements the number of threads waiting for this lock to be released. -+ */ -+ @YieldFree -+ public abstract void decrementWaitingThreads(); -+ -+ /** -+ * @return Whether this lock has any threads waiting for it. -+ */ -+ @YieldFree -+ protected abstract boolean hasWaitingThreads(); -+ -+ /** -+ * Sets {@link #canBeHeld} to false. -+ * This should be called immediately after construction of this {@link YieldingLock}. -+ * -+ * @return This instance. -+ */ -+ @YieldFree -+ public @NotNull YieldingLock withCanNotBeHeld() { -+ this.canBeHeld = false; -+ return this; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/AbstractTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AbstractTaskQueue.java -new file mode 100644 -index 0000000000000000000000000000000000000000..633a9b7998b304057d1780b2a4a1f0bc7160c6f6 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/AbstractTaskQueue.java -@@ -0,0 +1,102 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.queue; -+ -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.annotation.thread.BaseThreadOnly; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.ServerThread; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * An interface for a task queue that may contain tasks of specific {@link TaskSpan}s. -+ *
    -+ * All tasks must be non-blocking. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public interface AbstractTaskQueue { -+ -+ /** -+ * @return A name that the queue can be identified by. -+ */ -+ String getName(); -+ -+ /** -+ * @return Whether this queue can ever be polled from while yielding. -+ *
    -+ * Some queues should not be yielded to, because they contain tasks that may take excessively long. -+ */ -+ default boolean canBeYieldedTo() { -+ return true; -+ } -+ -+ /** -+ * @return Whether this queue has any tasks at all. -+ */ -+ boolean hasTasks(); -+ -+ /** -+ * @return Whether this queue has any tasks with the given {@link TaskSpan}. -+ */ -+ boolean hasTasks(TaskSpan span); -+ -+ /** -+ * @return Whether this queue supports tasks of the given {@link TaskSpan} at all. -+ */ -+ boolean canHaveTasks(TaskSpan span); -+ -+ /** -+ * Attempts to poll a task. -+ *
    -+ * This must not be called on queues in the {@link BaseTaskQueueTier#SERVER} tier by threads that are not an -+ * instance of {@link ServerThread}. -+ *
    -+ * This must not be called on queues apart from the {@link BaseTaskQueueTier#SERVER} tier by threads that are an -+ * instance of {@link ServerThread}. Use {@link #pollTiny(BaseThread)} instead. -+ * -+ * @param currentThread The current thread. -+ * @return The polled task, or null if this queue was empty. -+ */ -+ @BaseThreadOnly -+ @Nullable Runnable poll(BaseThread currentThread); -+ -+ /** -+ * Attempts to poll a {@link TaskSpan#TINY} task. -+ * -+ * @see #poll(BaseThread) -+ */ -+ @BaseThreadOnly -+ @Nullable Runnable pollTiny(BaseThread currentThread); -+ -+ /** -+ * Schedules a new task to this queue. -+ * -+ * @param task The task to schedule. -+ * @param span The {@link TaskSpan} of the task. -+ */ -+ void add(Runnable task, TaskSpan span); -+ -+ /** -+ * Sets the tier of this queue. This ensures queues do not accidentally initialize the tiers -+ * before the tiers have finished initializing themselves. -+ */ -+ void setTier(BaseTaskQueueTier tier); -+ -+ /** -+ * @return Whether any of the given task queues is non-empty. -+ */ -+ static boolean taskQueuesHaveTasks(AbstractTaskQueue[] queues) { -+ for (AbstractTaskQueue queue : queues) { -+ if (queue.hasTasks()) { -+ return true; -+ } -+ } -+ return false; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java -new file mode 100644 -index 0000000000000000000000000000000000000000..b4172f285fbed1f314891b2f729aa2dc27b9ab9b ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java -@@ -0,0 +1,133 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.queue; -+ -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.level.ServerLevel; -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.pool.BaseThreadActivation; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * Common implementation for queues with scheduled tasks for all levels. -+ *
    -+ * All tasks provided by this queue must be yield-free. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public abstract class AllLevelsScheduledTaskQueue implements AbstractTaskQueue { -+ -+ /** -+ * The {@link TaskSpan} of tasks in this queue. The span must be yield-free. -+ */ -+ public final TaskSpan span; -+ -+ /** -+ * Value of {@code onlyIfLastTimeIsTooLongAgo} in calls to -+ * {@link BaseThreadActivation#newTaskWasAdded(BaseTaskQueueTier, TaskSpan, boolean)}. -+ */ -+ private final boolean onlyNotifyBaseThreadPoolOfNewTasksIfLastTimeIsTooLongAgo; -+ -+ /** -+ * Will be initialized in {@link #setTier}. -+ */ -+ private BaseTaskQueueTier tier; -+ -+ /** -+ * An iteration index for iterating over the levels in {@link #poll}. -+ */ -+ private int levelIterationIndex; -+ -+ protected AllLevelsScheduledTaskQueue(TaskSpan span, boolean onlyNotifyBaseThreadPoolOfNewTasksIfLastTimeIsTooLongAgo) { -+ this.span = span; -+ this.onlyNotifyBaseThreadPoolOfNewTasksIfLastTimeIsTooLongAgo = onlyNotifyBaseThreadPoolOfNewTasksIfLastTimeIsTooLongAgo; -+ } -+ -+ protected abstract boolean hasLevelTasks(ServerLevel level); -+ -+ protected abstract @Nullable Runnable pollLevel(ServerLevel level); -+ -+ @Override -+ public boolean hasTasks() { -+ if (MinecraftServer.SERVER == null) { -+ return false; -+ } -+ for (ServerLevel level : MinecraftServer.SERVER.getAllLevels()) { -+ if (this.hasLevelTasks(level)) { -+ return true; -+ } -+ } -+ return false; -+ } -+ -+ @Override -+ public boolean hasTasks(TaskSpan span) { -+ return span == this.span && this.hasTasks(); -+ } -+ -+ @Override -+ public boolean canHaveTasks(TaskSpan span) { -+ return span == this.span; -+ } -+ -+ @Override -+ public @Nullable Runnable poll(BaseThread currentThread) { -+ // Skip during server bootstrap or if there is no more time in the current spare time -+ if (MinecraftServer.isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking || !MinecraftServer.isConstructed) { -+ return null; -+ } -+ if (this.span.isYielding && !currentThread.canStartYieldingTasks) { -+ return null; -+ } -+ ServerLevel[] levels = MinecraftServer.SERVER.getAllLevelsArray(); -+ int startIndex = this.levelIterationIndex = Math.min(this.levelIterationIndex, levels.length - 1); -+ // Paper - force execution of all worlds, do not just bias the first -+ do { -+ ServerLevel level = levels[this.levelIterationIndex++]; -+ if (this.levelIterationIndex == levels.length) { -+ this.levelIterationIndex = 0; -+ } -+ if (level.serverLevelArrayIndex != -1) { -+ Runnable task = this.pollLevel(level); -+ if (task != null) { -+ return task; -+ } -+ } -+ } while (this.levelIterationIndex != startIndex); -+ return null; -+ } -+ -+ @Override -+ public @Nullable Runnable pollTiny(BaseThread currentThread) { -+ if (this.span == TaskSpan.TINY) { -+ return this.poll(currentThread); -+ } -+ return null; -+ } -+ -+ @Override -+ public void add(Runnable task, TaskSpan span) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ /** -+ * To be called when a new task has been added to the underlying storage of this queue. -+ */ -+ public void newTaskWasAdded() { -+ BaseThreadActivation.newTaskWasAdded(this.tier, this.span, true, onlyNotifyBaseThreadPoolOfNewTasksIfLastTimeIsTooLongAgo); -+ } -+ -+ @Override -+ public void setTier(BaseTaskQueueTier tier) { -+ if (this.tier != null) { -+ throw new IllegalStateException(this.getClass().getSimpleName() + ".tier was already initialized"); -+ } -+ this.tier = tier; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/AnyTickScheduledServerThreadTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AnyTickScheduledServerThreadTaskQueue.java -new file mode 100644 -index 0000000000000000000000000000000000000000..690979cb9b7ec3dedbd7d0c45d0c183a2f56d2ec ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/AnyTickScheduledServerThreadTaskQueue.java -@@ -0,0 +1,27 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.queue; -+ -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.YieldFree; -+ -+/** -+ * This class provides access to, but does not store, the tasks scheduled to be executed on the main thread, -+ * that must be finished by some time in the future, but not necessarily within the current tick or its spare time. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public final class AnyTickScheduledServerThreadTaskQueue extends TickRequiredScheduledServerThreadTaskQueue { -+ -+ AnyTickScheduledServerThreadTaskQueue() { -+ super(true); -+ } -+ -+ @Override -+ public String getName() { -+ return "AnyTickScheduledServerThread"; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -new file mode 100644 -index 0000000000000000000000000000000000000000..7e24854f1e727e5e40108c68933466d0845bdca1 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -@@ -0,0 +1,127 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.queue; -+ -+import io.papermc.paper.util.TickThread; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.world.entity.Entity; -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.thread.AssistThread; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.ServerThread; -+ -+import java.util.Arrays; -+ -+/** -+ * A tier for {@link AbstractTaskQueue}s, that indicates the priority of the tasks in the task queues. -+ * Every tier contains a list of the queues that are part of the tier. -+ * The tiers are in order of priority, from high to low. -+ * Similarly, the queues for each tier are in the same order of priority. -+ * The tasks in each queue should also be in order of priority whenever relevant, but usually there -+ * is no strong difference in priority between tasks in the same queue, so they typically operate as FIFO queues, -+ * so that the longest waiting task implicitly has the highest priority within the queue. -+ *
    -+ * Tasks from queues in the {@link #SERVER} tier can only be run on a {@link ServerThread}. -+ * Tasks from other tiers can be run on {@link ServerThread}s as well as on {@link AssistThread}s. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public enum BaseTaskQueueTier { -+ -+ /** -+ * A tier for queues that contain tasks that must be executed on a {@link ServerThread}. -+ *
    -+ * Some parts of the server can only be safely accessed by one thread at a time. -+ * If they can not be guarded by a lock (or if this is not desired, -+ * because if a ticking thread would need to acquire this lock it would block it), -+ * then these parts of the code are typically deferred to the server thread. -+ * Based on the current use of the {@link TickThread} class, particularly given the existence of -+ * {@link TickThread#isTickThreadFor(Entity)} and {@link TickThread#isTickThreadFor(ServerLevel, int, int)}, -+ * we can deduce that future support for performing some of these actions in parallel is planned. -+ * In such a case, some server thread tasks may become tasks that must be -+ * executed on an appropriate {@link TickThread}. -+ * In that case, the queues below should be changed so that the server thread and any of the -+ * ticking threads poll from queues that contain tasks appropriate for them. -+ * For example, {@link BaseTaskQueues#deferredToUniversalTickThread} would be for tasks that can run -+ * on any ticking thread, and additional queues would need to be added concerning a specific -+ * subject (like an entity or chunk) with tasks that will be run on whichever ticking thread is the -+ * ticking thread for that subject at the time of polling. -+ *
    -+ * Note that a {@link ServerThread} can only yield to {@link TaskSpan#TINY} tasks in other tiers -+ * (since there are no higher tiers, and threads can only yield to lower tiers when -+ * the task yielded to is{@link TaskSpan#TINY}, or other non-yielding tasks in its own tier (since it -+ * has a {@link BaseThread#maximumYieldDepth} of 1). -+ * Yielding to other tasks in this same tier is somewhat risky, since this means that the tasks that were -+ * yielded to must assume that although they are running on the server thread, they may be running at -+ * some unknown point in execution of the main thread. Therefore, scheduling any non-yielding tasks to -+ * a queue in this tier must be done with the utmost care that the task cannot disrupt, or be disrupted by, -+ * the surrounding code that yields to it. -+ */ -+ SERVER(new AbstractTaskQueue[]{ -+ BaseTaskQueues.deferredToServerThread, -+ BaseTaskQueues.serverThreadTick, -+ BaseTaskQueues.anyTickScheduledServerThread -+ }, MinecraftServer.SERVER_THREAD_PRIORITY), -+ /** -+ * A tier for queues that contain tasks that are part of ticking, -+ * to assist the main ticking thread(s) in doing so. -+ */ -+ TICK_ASSIST(new AbstractTaskQueue[]{ -+ BaseTaskQueues.tickAssist -+ }, Integer.getInteger("gale.thread.priority.tick", 7)), -+ /** -+ * A tier for queues that contain general tasks that must be performed at some point in time, -+ * asynchronously with respect to the {@link ServerThread} and the ticking of the server. -+ * Execution of -+ */ -+ ASYNC(new AbstractTaskQueue[0], Integer.getInteger("gale.thread.priority.async", 6)), -+ /** -+ * A tier for queues that contain tasks with the same considerations as {@link #ASYNC}, -+ * but with a low priority. -+ */ -+ LOW_PRIORITY_ASYNC(new AbstractTaskQueue[0], Integer.getInteger("gale.thread.priority.async.low", 3)); -+ -+ /** -+ * Equal to {@link #ordinal()}. -+ */ -+ public final int ordinal; -+ -+ /** -+ * The task queues that belong to this tier. -+ */ -+ public final AbstractTaskQueue[] taskQueues; -+ -+ /** -+ * The priority for threads that are executing a task from this tier. -+ *
    -+ * If a thread yields to other tasks, the priority it will have is always the highest priority of any task -+ * on its stack. -+ */ -+ public final int threadPriority; -+ -+ BaseTaskQueueTier(AbstractTaskQueue[] taskQueues, int threadPriority) { -+ this.ordinal = this.ordinal(); -+ this.taskQueues = taskQueues; -+ for (AbstractTaskQueue queue : this.taskQueues) { -+ queue.setTier(this); -+ } -+ this.threadPriority = threadPriority; -+ } -+ -+ /** -+ * Equal to {@link #values()}. -+ */ -+ public static final BaseTaskQueueTier[] VALUES = values(); -+ -+ /** -+ * Equal to {@link #VALUES}{@code .length}. -+ */ -+ public static final int length = VALUES.length; -+ -+ /** -+ * Equal to {@link #VALUES} without {@link #SERVER}. -+ */ -+ public static final BaseTaskQueueTier[] VALUES_EXCEPT_SERVER = Arrays.stream(VALUES).filter(tier -> tier != SERVER).toList().toArray(new BaseTaskQueueTier[length - 1]); -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -new file mode 100644 -index 0000000000000000000000000000000000000000..cf8e2b42ecfc8205af5b105e19975c3e54ffec5f ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -@@ -0,0 +1,92 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.queue; -+ -+import io.papermc.paper.util.MCUtil; -+import io.papermc.paper.util.TickThread; -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.deferral.TickThreadDeferral; -+import org.galemc.gale.executor.thread.AbstractYieldingThread; -+ -+/** -+ * This class statically provides a list of task queues containing tasks for {@link AbstractYieldingThread}s to poll from. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public final class BaseTaskQueues { -+ -+ private BaseTaskQueues() {} -+ -+ /** -+ * This queue stores the tasks scheduled to be executed on a {@link TickThread}, that are procedures that must run -+ * on a tick thread, but their completion is currently needed by another task that has started running on a thread -+ * that was not the main thread at the time of scheduling the main-thread-only procedure. -+ *
    -+ * These tasks are explicitly those that other tasks are waiting on, and as such always have a higher priority -+ * in being started than pending tasks in represent steps in ticking the server, and as such always have the -+ * higher priority in being started than pending tasks in {@link #serverThreadTick}. -+ *
    -+ * This queue may contain tasks of every {@link TaskSpan}. -+ *
    -+ * This queue's {@link AbstractTaskQueue#add} must not be called from the server thread, -+ * because the server thread must not defer to itself (because tasks in this queue are assumed to have to run -+ * independent of other server thread tasks, therefore this will cause a deadlock due to the scheduled task -+ * never starting). -+ * Instead, any task that must be deferred to the main thread must instead simply be executed -+ * when encountered on the main thread. -+ */ -+ public static final SimpleTaskQueue deferredToServerThread = SimpleTaskQueue.allSpans("DeferredToServerThread", true); -+ -+ /** -+ * This queue stores the tasks scheduled to be executed on a {@link TickThread}, that are procedures that must run -+ * on a tick thread, but their completion is currently needed by another task that has started running on a thread -+ * that was not the main thread at the time of scheduling the main-thread-only procedure. -+ *
    -+ * This queue may contain tasks of every {@link TaskSpan}. -+ *
    -+ * This is currently completely unused, because {@link TickThreadDeferral} simply adds task to -+ * {@link #deferredToServerThread} instead, since there are currently no special {@link TickThread}s. -+ */ -+ @SuppressWarnings("unused") -+ public static final SimpleTaskQueue deferredToUniversalTickThread = SimpleTaskQueue.allSpans("DeferredToUniversalTickThread", true); -+ -+ /** -+ * This queue explicitly stores tasks that represent steps or parts of steps in ticking the server and that must be -+ * executed on the main thread, and as such always have a higher priority in being started than pending tasks in -+ * {@link #anyTickScheduledServerThread}. -+ *
    -+ * This queue may contain tasks of every {@link TaskSpan}. -+ *
    -+ * Tasks in every queue are performed in the order they are added (first-in, first-out). Note that this means -+ * not all main-thread-only tick tasks are necessarily performed in the order they are added, because they may be -+ * in different queues: either the queue for potentially yielding tasks or the queue for yield-free tasks. -+ */ -+ public static final SimpleTaskQueue serverThreadTick = SimpleTaskQueue.allSpans("ServerThreadTick"); -+ -+ /** -+ * Currently unused: only {@link #anyTickScheduledServerThread} is polled. -+ * -+ * @see ThisTickScheduledServerThreadTaskQueue -+ */ -+ @SuppressWarnings("unused") -+ public static final ThisTickScheduledServerThreadTaskQueue thisTickScheduledServerThread = null; -+ -+ /** -+ * @see AnyTickScheduledServerThreadTaskQueue -+ */ -+ public static final AnyTickScheduledServerThreadTaskQueue anyTickScheduledServerThread = new AnyTickScheduledServerThreadTaskQueue(); -+ -+ /** -+ * This queue explicitly stores tasks that represent steps or parts of steps in ticking the server that do not have -+ * to be executed on the main thread (but must be executed on a {@link BaseThread}). -+ *
    -+ * This queue may contain tasks of every {@link TaskSpan}. -+ *
    -+ * Tasks in every queue are performed in the order they are added (first-in, first-out). Note that this means -+ * not all {@link BaseThread} tick tasks are necessarily performed in the order they are added, because they may be -+ * in different queues: either the queue for potentially yielding tasks or the queue for yield-free tasks. -+ */ -+ public static final SimpleTaskQueue tickAssist = SimpleTaskQueue.allSpans("TickAssist"); -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/ScheduledServerThreadTaskQueues.java b/src/main/java/org/galemc/gale/executor/queue/ScheduledServerThreadTaskQueues.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a763028deff9bc131b8208886bf2651373b1dbc3 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/ScheduledServerThreadTaskQueues.java -@@ -0,0 +1,287 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.queue; -+ -+import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.util.thread.BlockableEventLoop; -+import org.galemc.gale.concurrent.UnterminableExecutorService; -+import org.galemc.gale.executor.annotation.Access; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.Guarded; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.thread.pool.BaseThreadActivation; -+import org.galemc.gale.executor.thread.ServerThread; -+import org.galemc.gale.executor.TaskSpan; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.Queue; -+import java.util.concurrent.Executor; -+import java.util.concurrent.ExecutorService; -+import java.util.concurrent.locks.Lock; -+import java.util.concurrent.locks.ReadWriteLock; -+import java.util.concurrent.locks.ReentrantReadWriteLock; -+ -+/** -+ * This class stores the tasks scheduled to be executed on the main thread, which would usually be stored in a queue -+ * in the {@link MinecraftServer} in its role as a {@link BlockableEventLoop}. These tasks are not steps of -+ * ticking, but other tasks that must be executed at some point while no other main-thread-only task is running. -+ *
    -+ * Each task is stored by the number of ticks left before it has to be executed. Tasks will always definitely be -+ * executed within the required number of ticks. Tasks may also be executed earlier than needed -+ * if there is time to do so. -+ *
    -+ * Note that this means not all tasks are necessarily performed in the order they are added, because they may be in -+ * different queues based on the number of ticks before they have to be executed. -+ *
    -+ * All contained tasks are currently assumed to be {@link TaskSpan#YIELDING}: no special distinction for more -+ * permissive task spans is made. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public final class ScheduledServerThreadTaskQueues { -+ -+ ScheduledServerThreadTaskQueues() {} -+ -+ public static int DEFAULT_TASK_MAX_DELAY = 2; -+ public static int COMPLETE_CHUNK_FUTURE_TASK_MAX_DELAY = DEFAULT_TASK_MAX_DELAY; -+ public static int POST_CHUNK_LOAD_JOIN_TASK_MAX_DELAY = DEFAULT_TASK_MAX_DELAY; -+ public static int ANTI_XRAY_MODIFY_BLOCKS_TASK_MAX_DELAY = DEFAULT_TASK_MAX_DELAY; -+ public static int TELEPORT_ASYNC_TASK_MAX_DELAY = DEFAULT_TASK_MAX_DELAY; -+ public static int SEND_COMMAND_COMPLETION_SUGGESTIONS_TASK_MAX_DELAY = DEFAULT_TASK_MAX_DELAY; -+ public static int KICK_FOR_COMMAND_PACKET_SPAM_TASK_MAX_DELAY = DEFAULT_TASK_MAX_DELAY; -+ public static int KICK_FOR_RECIPE_PACKET_SPAM_TASK_MAX_DELAY = DEFAULT_TASK_MAX_DELAY; -+ public static int KICK_FOR_BOOK_TOO_LARGE_PACKET_TASK_MAX_DELAY = DEFAULT_TASK_MAX_DELAY; -+ public static int KICK_FOR_EDITING_BOOK_TOO_QUICKLY_TASK_MAX_DELAY = DEFAULT_TASK_MAX_DELAY; -+ public static int KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY = DEFAULT_TASK_MAX_DELAY; -+ public static int KICK_FOR_OUT_OF_ORDER_CHAT_PACKET_TASK_MAX_DELAY = DEFAULT_TASK_MAX_DELAY; -+ public static int HANDLE_DISCONNECT_TASK_MAX_DELAY = DEFAULT_TASK_MAX_DELAY; -+ -+ /** -+ * Will be initialized in {@link TickRequiredScheduledServerThreadTaskQueue#setTier}. -+ */ -+ static BaseTaskQueueTier tier; -+ -+ /** -+ * A number of queues, with the queue at index i being the queue to be used after another i ticks pass -+ * even when {@link MinecraftServer#haveTime()} is false. -+ */ -+ @SuppressWarnings({"rawtypes", "GrazieInspection"}) -+ @Guarded(value = "#lock", except = "when optimistically reading using versionStamp as a stamp") -+ public static Queue[] queues = { new MultiThreadedQueue<>() }; -+ -+ /** -+ * Probably the lowest index of any queue in {@link #queues} that is non-empty. -+ * This is maintained as well as possible. -+ */ -+ public static volatile int firstQueueWithPotentialTasksIndex = 0; -+ -+ @Guarded(value = "#lock", fieldAccess = Access.WRITE) -+ public static volatile long versionStamp = 0; -+ -+ private static final ReadWriteLock lock = new ReentrantReadWriteLock(); -+ private static final Lock readLock = lock.readLock(); -+ public static final Lock writeLock = lock.writeLock(); -+ -+ /** -+ * @return Whether there are any scheduled main thread tasks, not counting any tasks that do not have to be -+ * finished within the current tick if {@code tryNonCurrentTickQueuesAtAll} is false. -+ */ -+ public static boolean hasTasks(boolean tryNonCurrentTickQueuesAtAll) { -+ while (!readLock.tryLock()) { -+ Thread.onSpinWait(); -+ } -+ try { -+ // Try the queue most likely to contain tasks first -+ if (firstQueueWithPotentialTasksIndex == 0 || tryNonCurrentTickQueuesAtAll) { -+ if (!queues[firstQueueWithPotentialTasksIndex].isEmpty()) { -+ return true; -+ } -+ } -+ int checkUpTo = tryNonCurrentTickQueuesAtAll ? queues.length : 1; -+ for (int i = 0; i < checkUpTo; i++) { -+ if (!queues[i].isEmpty()) { -+ return true; -+ } -+ } -+ } finally { -+ readLock.unlock(); -+ } -+ return false; -+ } -+ -+ /** -+ * Polls a task from this queue and returns it. -+ * Tasks that are scheduled for a later tick will only be regarded as able to be started if both we are not out of -+ * spare time this tick and {@code tryNonCurrentTickQueuesAtAll} is true. -+ *
    -+ * This method does not check whether the given thread is or could claim the main thread: whether a task -+ * can start now is thread-agnostic and based purely on the state of the queue. -+ * -+ * @return The task that was polled, or null if no task was found. -+ */ -+ public static @Nullable Runnable poll(ServerThread currentThread, boolean tryNonCurrentTickQueuesAtAll) { -+ // Since we assume the tasks in this queue to be potentially yielding, fail if the thread is restricted -+ if (!currentThread.canStartYieldingTasks) { -+ return null; -+ } -+ pollFromFirstQueueOrOthers: while (true) { -+ // Try to get a task from the first queue first -+ Object task = queues[0].poll(); -+ if (task != null) { -+ return (Runnable) task; -+ } else if (!tryNonCurrentTickQueuesAtAll || MinecraftServer.isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking) { -+ // We needed a task from the first queue -+ if (queues[0].isEmpty()) { -+ // The first queue really is empty, so we fail -+ return null; -+ } -+ // An element was added to the first queue in the meantime, try again -+ continue; -+ } -+ tryGetRunnableUntilSuccessOrNothingChanged: while (true) { -+ boolean goOverAllQueues = firstQueueWithPotentialTasksIndex == 0; -+ long oldVersionStamp = versionStamp; -+ for (int i = goOverAllQueues ? 0 : firstQueueWithPotentialTasksIndex; i < queues.length; i++) { -+ if (!queues[i].isEmpty()) { -+ if (i == 0 || !MinecraftServer.isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking) { -+ task = queues[i].poll(); -+ if (task == null) { -+ // We lost a race condition between the isEmpty() and poll() calls: just try again -+ continue tryGetRunnableUntilSuccessOrNothingChanged; -+ } -+ return (Runnable) task; -+ } -+ // Apparently we must now poll from the first queue only, try again -+ continue pollFromFirstQueueOrOthers; -+ } -+ // The queue was empty, the first queue with potential tasks must be the next one -+ if (i == firstQueueWithPotentialTasksIndex) { -+ if (i == queues.length - 1) { -+ firstQueueWithPotentialTasksIndex = 0; -+ } else { -+ firstQueueWithPotentialTasksIndex = i + 1; -+ } -+ } -+ } -+ if (goOverAllQueues && firstQueueWithPotentialTasksIndex == 0 && oldVersionStamp == versionStamp) { -+ /* -+ We went over all queues and nothing changed in the meantime, -+ we give up, there appear to be no more tasks. -+ */ -+ return null; -+ } -+ // Something changed, or we did not go over all queues, try again -+ } -+ } -+ } -+ -+ public static int getTaskCount() { -+ while (!readLock.tryLock()) { -+ Thread.onSpinWait(); -+ } -+ try { -+ int count = 0; -+ for (Queue queue : queues) { -+ count += queue.size(); -+ } -+ return count; -+ } finally { -+ readLock.unlock(); -+ } -+ } -+ -+ /** -+ * Schedules a new task to this queue. -+ * -+ * @param task The task to schedule. -+ * -+ * @deprecated Use {@link #add(Runnable, int)} instead: do not rely on {@link #DEFAULT_TASK_MAX_DELAY}. -+ */ -+ @Deprecated -+ public static void add(Runnable task) { -+ add(task, DEFAULT_TASK_MAX_DELAY); -+ } -+ -+ /** -+ * Schedules a new task to this queue. -+ * -+ * @param task The task to schedule. -+ * @param maxDelay The maximum number of ticks that the task must be finished by. -+ * A value of 0 means the task must be finished before the end of the current tick. -+ */ -+ public static void add(Runnable task, int maxDelay) { -+ // Paper start - anything that does try to post to main during watchdog crash, run on watchdog -+ if (MinecraftServer.SERVER.hasStopped && Thread.currentThread() == MinecraftServer.SERVER.shutdownThread) { -+ task.run(); -+ return; -+ } -+ // Paper end - anything that does try to post to main during watchdog crash, run on watchdog -+ while (!readLock.tryLock()) { -+ Thread.onSpinWait(); -+ } -+ try { -+ //noinspection NonAtomicOperationOnVolatileField -+ versionStamp++; -+ //noinspection unchecked -+ queues[maxDelay].add(task); -+ if (maxDelay < firstQueueWithPotentialTasksIndex) { -+ firstQueueWithPotentialTasksIndex = maxDelay; -+ } -+ //noinspection NonAtomicOperationOnVolatileField -+ versionStamp++; -+ } finally { -+ readLock.unlock(); -+ } -+ MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks = true; -+ BaseThreadActivation.newTaskWasAdded(tier, TaskSpan.YIELDING); -+ } -+ -+ /** -+ * This moves the queues in {@link #queues}. -+ */ -+ public static void shiftTasksForNextTick() { -+ while (!writeLock.tryLock()) { -+ Thread.onSpinWait(); -+ } -+ try { -+ //noinspection NonAtomicOperationOnVolatileField -+ versionStamp++; -+ // Move the queues to the preceding position -+ Queue firstQueue = queues[0]; -+ for (int i = 1; i < queues.length; i++) { -+ queues[i - 1] = queues[i]; -+ } -+ // Re-use the same instance that was the old first queue as the new last queue -+ queues[queues.length - 1] = firstQueue; -+ // Move any elements that were still present in the previous first queue to the new first queue -+ //noinspection unchecked -+ queues[0].addAll(firstQueue); -+ firstQueue.clear(); -+ //noinspection NonAtomicOperationOnVolatileField -+ versionStamp++; -+ } finally { -+ writeLock.unlock(); -+ } -+ } -+ -+ /** -+ * An executor for adding tasks to this queue, -+ * where {@link Executor#execute} calls {@link #add}. -+ * -+ * @deprecated Use {@link #add(Runnable, int)} instead: do not rely on {@link #DEFAULT_TASK_MAX_DELAY}. -+ */ -+ @Deprecated -+ public static final ExecutorService executor = new UnterminableExecutorService() { -+ -+ @Override -+ public void execute(@NotNull Runnable runnable) { -+ add(runnable); -+ } -+ -+ }; -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/SimpleTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/SimpleTaskQueue.java -new file mode 100644 -index 0000000000000000000000000000000000000000..fa9f7f248a46fa7a4a778e06320164df9cafe3b2 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/SimpleTaskQueue.java -@@ -0,0 +1,253 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.queue; -+ -+import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue; -+import org.galemc.gale.collection.FIFOConcurrentLinkedQueue; -+import org.galemc.gale.concurrent.UnterminableExecutorService; -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.pool.BaseThreadActivation; -+import org.galemc.gale.executor.thread.platform.SignalReason; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.Arrays; -+import java.util.Queue; -+import java.util.concurrent.Executor; -+import java.util.concurrent.ExecutorService; -+ -+/** -+ * A base class for a task queue that may contain tasks that are -+ * potentially yielding and tasks that are yield-free. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public class SimpleTaskQueue implements AbstractTaskQueue { -+ -+ /** -+ * The name of this queue. -+ * -+ * @see #getName() -+ */ -+ public final String name; -+ -+ /** -+ * The {@link BaseTaskQueueTier} that contains this queue. -+ * Will be initialized in {@link #setTier} by the constructor of the tier. -+ */ -+ public BaseTaskQueueTier tier; -+ -+ /** -+ * Whether tasks in this queue can have a {@link TaskSpan}, indexed by the span's {@link TaskSpan#ordinal}. -+ */ -+ public final boolean[] canHaveTasks; -+ -+ /** -+ * The queues of tasks, indexed by their {@link TaskSpan#ordinal}. Individual elements may be null if the -+ * corresponding {@link #canHaveTasks} value is false. -+ */ -+ @SuppressWarnings("rawtypes") -+ private final @Nullable Queue @NotNull [] queues; -+ -+ /** -+ * An executor for adding tasks of a specific {@link TaskSpan} to this queue, where {@link Executor#execute} -+ * calls {@link #add(Runnable, TaskSpan)}, -+ * or null if the corresponding value in {@link #canHaveTasks} is false. -+ */ -+ @SuppressWarnings("FieldCanBeLocal") -+ private final @Nullable ExecutorService @NotNull [] executors; -+ -+ /** -+ * An executor for adding {@link TaskSpan#YIELDING} tasks to this queue, -+ * where {@link Executor#execute} calls {@link #add}, -+ * or null if the corresponding value in {@link #canHaveTasks} is false. -+ */ -+ public final ExecutorService yieldingExecutor; -+ -+ /** -+ * An executor for adding {@link TaskSpan#FREE} tasks to this queue, -+ * where {@link Executor#execute} calls {@link #add}, -+ * or null if the corresponding value in {@link #canHaveTasks} is false. -+ */ -+ public final ExecutorService freeExecutor; -+ -+ /** -+ * An executor for adding {@link TaskSpan#TINY} tasks to this queue, -+ * where {@link Executor#execute} calls {@link #add}, -+ * or null if the corresponding value in {@link #canHaveTasks} is false. -+ */ -+ public final ExecutorService tinyExecutor; -+ -+ SimpleTaskQueue(String name, boolean[] canHaveTasks) { -+ this(name, canHaveTasks, false); -+ } -+ -+ /** -+ * @param name Value for {@link #getName}. -+ * @param canHaveTasks Value for {@link #canHaveTasks}. -+ * @param lifoQueues If true, the queues in {@link #queues} will be LIFO; -+ * otherwise, they will be FIFO. -+ */ -+ SimpleTaskQueue(String name, boolean[] canHaveTasks, boolean lifoQueues) { -+ this.name = name; -+ if (canHaveTasks.length != TaskSpan.length) { -+ throw new IllegalArgumentException(); -+ } -+ this.canHaveTasks = canHaveTasks; -+ this.queues = new Queue[TaskSpan.length]; -+ this.executors = new ExecutorService[TaskSpan.length]; -+ for (int spanOrdinal = 0; spanOrdinal < TaskSpan.length; spanOrdinal++) { -+ if (this.canHaveTasks[spanOrdinal]) { -+ this.queues[spanOrdinal] = lifoQueues ? new FIFOConcurrentLinkedQueue<>() : new MultiThreadedQueue<>(); -+ this.executors[spanOrdinal] = new SpanExecutor(TaskSpan.VALUES[spanOrdinal]); -+ } -+ } -+ this.yieldingExecutor = this.executors[TaskSpan.YIELDING.ordinal]; -+ this.freeExecutor = this.executors[TaskSpan.FREE.ordinal]; -+ this.tinyExecutor = this.executors[TaskSpan.TINY.ordinal]; -+ } -+ -+ @Override -+ public final String getName() { -+ return this.name; -+ } -+ -+ @Override -+ public final boolean hasTasks() { -+ for (int spanOrdinal = 0; spanOrdinal < TaskSpan.length; spanOrdinal++) { -+ var queue = this.queues[spanOrdinal]; -+ if (queue != null && !queue.isEmpty()) { -+ return true; -+ } -+ } -+ return false; -+ } -+ -+ @Override -+ public boolean hasTasks(TaskSpan span) { -+ var queue = this.queues[span.ordinal]; -+ return queue != null && !queue.isEmpty(); -+ } -+ -+ @Override -+ public boolean canHaveTasks(TaskSpan span) { -+ return this.queues[span.ordinal] != null; -+ } -+ -+ /** -+ * @see #poll(BaseThread) -+ */ -+ private @Nullable Runnable poll(BaseThread currentThread, int spanOrdinal) { -+ var queue = this.queues[spanOrdinal]; -+ if (queue != null) { -+ if (currentThread.canStartYieldingTasks || TaskSpan.VALUES[spanOrdinal].isNotYielding) { -+ Object task = queue.poll(); -+ if (task != null) { -+ /* -+ If the thread was woken up for a different reason, -+ another thread should be signalled for that reason. -+ */ -+ SignalReason lastSignalReason = currentThread.lastSignalReason; -+ if (lastSignalReason != null && lastSignalReason != SignalReason.TASK) { -+ BaseThreadActivation.callForUpdate(); -+ } -+ return (Runnable) task; -+ } -+ } -+ } -+ return null; -+ } -+ -+ @Override -+ public final @Nullable Runnable poll(BaseThread currentThread) { -+ Runnable task; -+ for (int spanOrdinal = 0; spanOrdinal < TaskSpan.length; spanOrdinal++) { -+ task = this.poll(currentThread, spanOrdinal); -+ if (task != null) { -+ return task; -+ } -+ } -+ return null; -+ } -+ -+ @Override -+ public final @Nullable Runnable pollTiny(BaseThread currentThread) { -+ return poll(currentThread, TaskSpan.TINY.ordinal); -+ } -+ -+ @Override -+ public final void add(Runnable task, TaskSpan span) { -+ int spanOrdinal = span.ordinal; -+ //noinspection unchecked -+ this.queues[spanOrdinal].add(task); -+ BaseThreadActivation.newTaskWasAdded(this.tier, span); -+ } -+ -+ @Override -+ public final void setTier(BaseTaskQueueTier tier) { -+ if (this.tier != null) { -+ throw new IllegalStateException("SimpleTaskQueue.tier was already initialized"); -+ } -+ this.tier = tier; -+ } -+ -+ private class SpanExecutor extends UnterminableExecutorService { -+ -+ private final TaskSpan span; -+ -+ private SpanExecutor(TaskSpan span) { -+ this.span = span; -+ } -+ -+ @Override -+ public void execute(@NotNull Runnable runnable) { -+ SimpleTaskQueue.this.add(runnable, this.span); -+ } -+ -+ } -+ -+ public static SingleSpanSimpleTaskQueue singleSpan(String name, TaskSpan span) { -+ return new SingleSpanSimpleTaskQueue(name, span); -+ } -+ -+ @SuppressWarnings("unused") -+ public static SingleSpanSimpleTaskQueue singleSpan(String name, TaskSpan span, boolean lifoQueues) { -+ return new SingleSpanSimpleTaskQueue(name, span, lifoQueues); -+ } -+ -+ private static boolean[] createCanHaveTasksForDoubleSpan(TaskSpan span1, TaskSpan span2) { -+ boolean[] canHaveTasks = new boolean[TaskSpan.length]; -+ canHaveTasks[span1.ordinal] = true; -+ canHaveTasks[span2.ordinal] = true; -+ return canHaveTasks; -+ } -+ -+ @SuppressWarnings("unused") -+ public static SimpleTaskQueue doubleSpan(String name, TaskSpan span1, TaskSpan span2) { -+ return new SimpleTaskQueue(name, createCanHaveTasksForDoubleSpan(span1, span2)); -+ } -+ -+ @SuppressWarnings("unused") -+ public static SimpleTaskQueue doubleSpan(String name, TaskSpan span1, TaskSpan span2, boolean lifoQueues) { -+ return new SimpleTaskQueue(name, createCanHaveTasksForDoubleSpan(span1, span2), lifoQueues); -+ } -+ -+ private static final boolean[] canHaveTasksForAllSpans = new boolean[TaskSpan.length]; -+ static { -+ Arrays.fill(canHaveTasksForAllSpans, true); -+ } -+ -+ public static SimpleTaskQueue allSpans(String name) { -+ return new SimpleTaskQueue(name, canHaveTasksForAllSpans); -+ } -+ -+ public static SimpleTaskQueue allSpans(String name, boolean lifoQueues) { -+ return new SimpleTaskQueue(name, canHaveTasksForAllSpans, lifoQueues); -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/SingleSpanSimpleTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/SingleSpanSimpleTaskQueue.java -new file mode 100644 -index 0000000000000000000000000000000000000000..2251087670d554a7bd5dc81631615aa0728eb315 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/SingleSpanSimpleTaskQueue.java -@@ -0,0 +1,66 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.queue; -+ -+import org.galemc.gale.concurrent.UnterminableExecutorService; -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.concurrent.Executor; -+import java.util.concurrent.ExecutorService; -+ -+/** -+ * A base class for a task queue that contains tasks that are all of one {@link TaskSpan}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@YieldFree -+public class SingleSpanSimpleTaskQueue extends SimpleTaskQueue { -+ -+ private static final boolean[][] canHaveTasksPerSpan = new boolean[TaskSpan.length][]; -+ static { -+ for (TaskSpan span : TaskSpan.VALUES) { -+ canHaveTasksPerSpan[span.ordinal] = new boolean[TaskSpan.length]; -+ canHaveTasksPerSpan[span.ordinal][span.ordinal] = true; -+ } -+ } -+ -+ private final TaskSpan span; -+ -+ SingleSpanSimpleTaskQueue(String name, TaskSpan span) { -+ super(name, canHaveTasksPerSpan[span.ordinal]); -+ this.span = span; -+ } -+ -+ SingleSpanSimpleTaskQueue(String name, TaskSpan span, boolean lifoQueues) { -+ super(name, canHaveTasksPerSpan[span.ordinal], lifoQueues); -+ this.span = span; -+ } -+ -+ /** -+ * Schedules a new task to this queue. -+ * -+ * @param task The task to schedule. -+ */ -+ @AnyThreadSafe -+ @YieldFree -+ public void add(Runnable task) { -+ this.add(task, this.span); -+ } -+ -+ /** -+ * An executor for adding tasks to this queue, -+ * where {@link Executor#execute} calls {@link #add}. -+ */ -+ public final ExecutorService executor = new UnterminableExecutorService() { -+ -+ @Override -+ public void execute(@NotNull Runnable runnable) { -+ SingleSpanSimpleTaskQueue.this.add(runnable, SingleSpanSimpleTaskQueue.this.span); -+ } -+ -+ }; -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/ThisTickScheduledServerThreadTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/ThisTickScheduledServerThreadTaskQueue.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3cb2b84cb7653ff3e038acdc2e6e11f805bfbbba ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/ThisTickScheduledServerThreadTaskQueue.java -@@ -0,0 +1,27 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.queue; -+ -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.YieldFree; -+ -+/** -+ * This class provides access to, but does not store, the tasks scheduled to be executed on the main thread, -+ * that must be finished in the current tick or its spare time. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public final class ThisTickScheduledServerThreadTaskQueue extends TickRequiredScheduledServerThreadTaskQueue { -+ -+ ThisTickScheduledServerThreadTaskQueue() { -+ super(false); -+ } -+ -+ @Override -+ public String getName() { -+ return "ThisTickScheduledServerThread"; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/TickRequiredScheduledServerThreadTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/TickRequiredScheduledServerThreadTaskQueue.java -new file mode 100644 -index 0000000000000000000000000000000000000000..fb4f9c047fc71a9a01aa47871254c6a949753a3a ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/TickRequiredScheduledServerThreadTaskQueue.java -@@ -0,0 +1,67 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.queue; -+ -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.ServerThread; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * A common base class for {@link ThisTickScheduledServerThreadTaskQueue} and -+ * {@link AnyTickScheduledServerThreadTaskQueue}. -+ *
    -+ * This queue does not support {@link #add}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public abstract class TickRequiredScheduledServerThreadTaskQueue implements AbstractTaskQueue { -+ -+ private final boolean tryNonCurrentTickQueuesAtAll; -+ -+ protected TickRequiredScheduledServerThreadTaskQueue(boolean tryNonCurrentTickQueuesAtAll) { -+ this.tryNonCurrentTickQueuesAtAll = tryNonCurrentTickQueuesAtAll; -+ } -+ -+ @Override -+ public boolean hasTasks() { -+ return ScheduledServerThreadTaskQueues.hasTasks(this.tryNonCurrentTickQueuesAtAll); -+ } -+ -+ @Override -+ public boolean hasTasks(TaskSpan span) { -+ return span == TaskSpan.YIELDING && this.hasTasks(); -+ } -+ -+ @Override -+ public boolean canHaveTasks(TaskSpan span) { -+ return span == TaskSpan.YIELDING; -+ } -+ -+ @Override -+ public @Nullable Runnable poll(BaseThread currentThread) { -+ return ScheduledServerThreadTaskQueues.poll((ServerThread) currentThread, this.tryNonCurrentTickQueuesAtAll); -+ } -+ -+ @Override -+ public @Nullable Runnable pollTiny(BaseThread currentThread) { -+ return null; -+ } -+ -+ @Override -+ public void add(Runnable task, TaskSpan span) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public void setTier(BaseTaskQueueTier tier) { -+ if (ScheduledServerThreadTaskQueues.tier == null) { -+ ScheduledServerThreadTaskQueues.tier = tier; -+ } -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/thread/AbstractYieldingThread.java b/src/main/java/org/galemc/gale/executor/thread/AbstractYieldingThread.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a21091bdee3521f4c37ed67cf6db72ac4bdf7a50 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/thread/AbstractYieldingThread.java -@@ -0,0 +1,146 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.thread; -+ -+import org.galemc.gale.concurrent.CheckableLock; -+import org.galemc.gale.executor.annotation.PotentiallyYielding; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.thread.ThisThreadOnly; -+import org.galemc.gale.executor.lock.SingleWaitingBaseThreadYieldingLock; -+import org.galemc.gale.executor.lock.YieldingLock; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.TimeUnit; -+import java.util.concurrent.locks.Condition; -+import java.util.function.BooleanSupplier; -+import java.util.function.Consumer; -+ -+/** -+ * An interface for threads that can yield to other tasks, for example upon encountering a {@link YieldingLock}, -+ * in lieu of blocking. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public interface AbstractYieldingThread extends SignallableThread { -+ -+ /** -+ * @return Whether this thread currently holds any {@link YieldingLock}. -+ */ -+ @ThisThreadOnly -+ @YieldFree -+ boolean holdsYieldingLock(); -+ -+ /** -+ * Increments the number of times this thread is holding a {@link YieldingLock}. -+ * A thread can hold one {@link YieldingLock} multiple times (it can be reentrant). -+ */ -+ @ThisThreadOnly -+ @YieldFree -+ void incrementHeldYieldingLockCount(); -+ -+ /** -+ * Decrements the number of times this thread is holding a {@link YieldingLock}. -+ * -+ * @see #incrementHeldYieldingLockCount() -+ */ -+ @ThisThreadOnly -+ @YieldFree -+ void decrementHeldYieldingLockCount(); -+ -+ @ThisThreadOnly -+ @PotentiallyYielding("this method is meant to yield") -+ void yieldUntil(@Nullable Long timeoutTime, @Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock); -+ -+ /** -+ * Calls {@link #yieldUntil(Long, BooleanSupplier, YieldingLock)}, but creates a {@link YieldingLock} -+ * based on the given {@code future}, that causes this thread to be notified when the future is completed. -+ * -+ * @param autoCompletingLockConsumer An optional consumer that will be applied to the created {@link YieldingLock}, -+ * so that {@link YieldingLock#unlock} can be called on it -+ * when the {@code stopCondition} becomes true. -+ */ -+ @ThisThreadOnly -+ @PotentiallyYielding("this method is meant to yield") -+ default void yieldUntilFuture(@Nullable Long timeoutTime, @Nullable BooleanSupplier stopCondition, @NotNull CompletableFuture future, @Nullable Consumer autoCompletingLockConsumer) { -+ -+ /* -+ Here, we abuse the Lock interface to create a YieldingLock -+ that can be speculatively locked only while tasks for this blockable event loop are available, -+ or when the future has completed. -+ */ -+ YieldingLock autoCompletingLock = new SingleWaitingBaseThreadYieldingLock(new CheckableLock() { -+ -+ @Override -+ public boolean isLocked() { -+ return !stopCondition.getAsBoolean() && !future.isDone(); -+ } -+ -+ @Override -+ public boolean isHeldByCurrentThread() { -+ throw new UnsupportedOperationException("isHeldByCurrentThread() is not supported for a yieldUntilFuture lock"); -+ } -+ -+ @Override -+ public void lock() { -+ throw new UnsupportedOperationException("lock() is not supported for a yieldUntilFuture lock"); -+ } -+ -+ @Override -+ public void lockInterruptibly() { -+ throw new UnsupportedOperationException("lockInterruptibly() is not supported for a yieldUntilFuture lock"); -+ } -+ -+ @Override -+ public boolean tryLock() { -+ return !this.isLocked(); -+ } -+ -+ @Override -+ public boolean tryLock(long time, @NotNull TimeUnit unit) throws InterruptedException { -+ throw new UnsupportedOperationException("tryLock(long, TimeUnit) is not supported for a yieldUntilFuture lock"); -+ } -+ -+ @Override -+ public void unlock() {} -+ -+ @NotNull -+ @Override -+ public Condition newCondition() { -+ throw new UnsupportedOperationException("newCondition() is not supported for a yieldUntilFuture lock"); -+ } -+ -+ }).withCanNotBeHeld(); -+ if (autoCompletingLockConsumer != null) { -+ autoCompletingLockConsumer.accept(autoCompletingLock); -+ } -+ // Be properly notified when the future completes -+ future.thenRun(autoCompletingLock::unlock); -+ -+ // Yield while necessary -+ this.yieldUntil(timeoutTime, null, autoCompletingLock); -+ -+ } -+ -+ @ThisThreadOnly -+ void runTasksUntil(@Nullable Long timeoutTime, @Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock); -+ -+ @AnyThreadSafe -+ @YieldFree -+ static @Nullable AbstractYieldingThread currentYieldingThread() { -+ return Thread.currentThread() instanceof AbstractYieldingThread yieldingThread ? yieldingThread : null; -+ } -+ -+ /** -+ * @return Whether the current thread is a {@link AbstractYieldingThread}. -+ */ -+ @SuppressWarnings("unused") -+ @AnyThreadSafe -+ @YieldFree -+ static boolean isYieldingThread() { -+ return Thread.currentThread() instanceof AbstractYieldingThread; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/thread/AssistThread.java b/src/main/java/org/galemc/gale/executor/thread/AssistThread.java -new file mode 100644 -index 0000000000000000000000000000000000000000..eab769d7319f26db1f4db9599a3c263c507641bd ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/thread/AssistThread.java -@@ -0,0 +1,78 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.thread; -+ -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.thread.BaseThreadOnly; -+import org.galemc.gale.executor.annotation.thread.ThisThreadOnly; -+import org.galemc.gale.executor.thread.pool.BaseThreadPool; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * A thread created by the {@link BaseThreadPool}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public class AssistThread extends BaseThread { -+ -+ /** -+ * The maximum yield depth. While an {@link AssistThread} has a yield depth equal to or greater than this value, -+ * it can not start more potentially yielding tasks. -+ */ -+ public static final int MAXIMUM_YIELD_DEPTH = Integer.getInteger("gale.yield.depth.max", 100); -+ -+ /** -+ * The index of this thread, as needed as an argument to -+ * {@link BaseThreadPool#getThreadByAssistIndex(int)}. -+ */ -+ public final int assistThreadIndex; -+ -+ /** -+ * Must only be called from {@link BaseThreadPool#addAssistThread}. -+ */ -+ public AssistThread(int assistThreadIndex) { -+ super(AssistThread::getCurrentAssistThreadAndRunForever, "Assist Thread " + assistThreadIndex, assistThreadIndex + 1, MAXIMUM_YIELD_DEPTH); -+ this.assistThreadIndex = assistThreadIndex; -+ } -+ -+ /** -+ * Causes this thread to loop forever, always attempting to find a task to do, and if none is found, -+ * registering itself with the places where a relevant task may be added in order to be signalled when -+ * one is actually added. -+ */ -+ @ThisThreadOnly -+ protected void runForever() { -+ this.runTasksUntil(null, () -> false, null); -+ } -+ -+ /** -+ * @return The current thread if it is a {@link AssistThread}, or null otherwise. -+ */ -+ @SuppressWarnings("unused") -+ @AnyThreadSafe -+ @YieldFree -+ public static @Nullable AssistThread currentAssistThread() { -+ return Thread.currentThread() instanceof AssistThread assistThread ? assistThread : null; -+ } -+ -+ /** -+ * @return Whether the current thread is a {@link AssistThread}. -+ */ -+ @SuppressWarnings("unused") -+ @AnyThreadSafe -+ @YieldFree -+ public static boolean isAssistThread() { -+ return Thread.currentThread() instanceof AssistThread; -+ } -+ -+ /** -+ * A method that simply acquires the {@link AssistThread} that is the current thread, and calls -+ * {@link #runForever()} on it. -+ */ -+ @BaseThreadOnly -+ protected static void getCurrentAssistThreadAndRunForever() { -+ ((AssistThread) Thread.currentThread()).runForever(); -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/thread/BaseThread.java b/src/main/java/org/galemc/gale/executor/thread/BaseThread.java -new file mode 100644 -index 0000000000000000000000000000000000000000..cb1731f8c00f30a18aa042eaabc7f429fcaf9e2a ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/thread/BaseThread.java -@@ -0,0 +1,766 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.thread; -+ -+import io.papermc.paper.util.TickThread; -+import net.minecraft.server.MinecraftServer; -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.annotation.Access; -+import org.galemc.gale.executor.annotation.PotentiallyYielding; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.Guarded; -+import org.galemc.gale.executor.annotation.PotentiallyBlocking; -+import org.galemc.gale.executor.annotation.thread.ThisThreadOnly; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.lock.YieldingLock; -+import org.galemc.gale.executor.queue.AbstractTaskQueue; -+import org.galemc.gale.executor.queue.BaseTaskQueueTier; -+import org.galemc.gale.executor.thread.platform.SignalReason; -+import org.galemc.gale.executor.thread.pool.*; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.concurrent.TimeUnit; -+import java.util.concurrent.locks.Condition; -+import java.util.concurrent.locks.Lock; -+import java.util.concurrent.locks.ReentrantLock; -+import java.util.function.BooleanSupplier; -+ -+/** -+ * An abstract base class implementing {@link AbstractYieldingThread}, -+ * that provides implementation that is common between -+ * {@link TickThread} and {@link AssistThread}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public abstract class BaseThread extends Thread implements AbstractYieldingThread { -+ -+ /** -+ * The minimum time to wait as the {@link MinecraftServer#serverThread} when performing a timed wait. -+ * Given in nanoseconds. -+ * If a timed wait with a lower time is attempted, the wait is not performed at all. -+ */ -+ public static final long SERVER_THREAD_WAIT_NANOS_MINIMUM = 10_000; -+ -+ /** -+ * The time to wait as the {@link MinecraftServer#serverThread} during the oversleep phase, if -+ * there may be delayed tasks. -+ * Given in nanoseconds. -+ */ -+ public static final long SERVER_THREAD_WAIT_NANOS_DURING_OVERSLEEP_WITH_DELAYED_TASKS = 50_000; -+ -+ /** -+ * The index of this thread, as needed as an argument to -+ * {@link BaseThreadPool#getThreadByBaseIndex(int)}. -+ */ -+ public final int baseThreadIndex; -+ -+ /** -+ * The maximum yield depth for this thread, -+ * which equals 1 for a {@link ServerThread} -+ * and {@link AssistThread#MAXIMUM_YIELD_DEPTH} for an {@link AssistThread}. -+ */ -+ public final int maximumYieldDepth; -+ -+ /** -+ * The number of times this thread holds a {@link YieldingLock}, -+ * used in {@link #holdsYieldingLock()}. -+ * -+ * @see AbstractYieldingThread#incrementHeldYieldingLockCount() -+ */ -+ @ThisThreadOnly -+ public int heldYieldingLockCount = 0; -+ -+ /** -+ * The current yield depth of this thread. -+ */ -+ @AnyThreadSafe(Access.READ) @ThisThreadOnly(Access.WRITE) -+ public volatile int yieldDepth = 0; -+ -+ /** -+ * Whether this thread can currently start yielding tasks with respect to being restricted -+ * due to {@link #yieldDepth} being at least {@link #maximumYieldDepth}. -+ *
    -+ * This is updated using {@link #updateCanStartYieldingTasks()} -+ * after {@link #yieldDepth} or {@link #heldYieldingLockCount} is changed. -+ */ -+ @AnyThreadSafe(Access.READ) @ThisThreadOnly(Access.WRITE) -+ public volatile boolean canStartYieldingTasks = true; -+ -+ /** -+ * The highest {@link BaseTaskQueueTier} of any task on the yielding execution stack of this thread, -+ * or null if there is no task being executed on this thread. -+ */ -+ @AnyThreadSafe(Access.READ) @ThisThreadOnly(Access.WRITE) -+ public volatile @Nullable BaseTaskQueueTier highestTierOfTaskOnStack; -+ -+ /** -+ * The {@link BaseTaskQueueTier} that the last non-null return value of {@link #pollTask} was polled from, -+ * or null if {@link #pollTask} has never been called yet. -+ */ -+ @ThisThreadOnly -+ private @Nullable BaseTaskQueueTier lastPolledTaskTier; -+ -+ /** -+ * The lock to guard this thread's sleeping and waking actions. -+ */ -+ private final Lock waitLock = new ReentrantLock(); -+ -+ /** -+ * The condition to wait for a signal, when this thread has to wait for something to do. -+ */ -+ private final Condition waitCondition = waitLock.newCondition(); -+ -+ /** -+ * Whether this thread is currently not working on the content of a task, but instead -+ * attempting to poll a next task to do, checking whether it can accept tasks at all, or -+ * attempting to acquire a {@link YieldingLock}, or waiting (although the fact that this value is true during -+ * waiting is irrelevant, because at such a time, {@link #isWaiting} will be true, and this value will no longer -+ * have any effect due to the implementation of {@link #signal}). -+ *
    -+ * This value is used to determine whether to set {@link #skipNextWait} when {@link #signal} is called -+ * and {@link #isWaiting} is false. -+ */ -+ @AnyThreadSafe(Access.READ) @ThisThreadOnly(Access.WRITE) -+ private volatile boolean isPollingTaskOrCheckingStopCondition = true; -+ -+ /** -+ * Whether this thread should not start waiting for something to do the next time no task could be polled, -+ * but instead try polling a task again. -+ */ -+ @AnyThreadSafe -+ public volatile boolean skipNextWait = false; -+ -+ /** -+ * Whether this thread is currently waiting for something to do. -+ *
    -+ * This is set to true at some point before actually starting to wait in a blocking fashion, -+ * and set to false at some point after no longer waiting in a blocking fashion. So, at some point, -+ * this value may be true while the thread is not blocked yet, or anymore. -+ * Even more so, extra checks for whether the thread should block will be performed in between -+ * the moment this value is set to true and the moment the thread potentially blocks. This means that if the -+ * checks fail, this value may be set to true and then false again, without actually ever blocking. -+ */ -+ @AnyThreadSafe(Access.READ) @ThisThreadOnly(Access.WRITE) -+ @Guarded(value = "#waitLock", fieldAccess = Access.WRITE) -+ public volatile boolean isWaiting = false; -+ -+ /** -+ * Whether {@link #isWaiting} is irrelevant because this thread has already -+ * been signalled via {@link #signal} to wake up. -+ */ -+ @AnyThreadSafe(Access.READ) @ThisThreadOnly(Access.WRITE) -+ @Guarded(value = "#waitLock", fieldAccess = Access.WRITE) -+ public volatile boolean mayBeStillWaitingButHasBeenSignalled = false; -+ -+ /** -+ * The {@link YieldingLock} that this thread is waiting for, -+ * or null if this thread is not waiting for a {@link YieldingLock}. -+ * This value only has meaning while {@link #isWaiting} is true. -+ */ -+ @AnyThreadSafe(Access.READ) @ThisThreadOnly(Access.WRITE) -+ @Guarded(value = "#waitLock", fieldAccess = Access.WRITE) -+ public volatile @Nullable YieldingLock lockWaitingFor = null; -+ -+ /** -+ * The value of {@link #lockWaitingFor} during the last wait (a call to {@link Condition#await}) -+ * or pre-wait check (while {@link #isNotActuallyWaitingYet} is true). -+ */ -+ @ThisThreadOnly -+ private @Nullable YieldingLock lastLockWaitedFor = null; -+ -+ /** -+ * A special flag, used after changing {@link #isWaiting}, when the lock must be temporarily released to -+ * call {@link BaseThreadActivation#callForUpdate()} (to avoid deadlocks in {@link #signal} calls), -+ * and we wish the pool to regard this thread as waiting -+ * (which it will, because {@link #isWaiting} will be true), but we must still -+ * know not to signal the underlying {@link #waitCondition}, but set {@link #skipNextWait} to true, -+ * when {@link #signal} is called at some point during the short release of {@link #waitLock}. -+ */ -+ public volatile boolean isNotActuallyWaitingYet = false; -+ -+ /** -+ * The last reason this thread was signalled before the current poll attempt, or null if the current -+ * poll attempt was not preceded by signalling (but by yielding for example). -+ */ -+ public volatile @Nullable SignalReason lastSignalReason = null; -+ -+ protected BaseThread(Runnable target, String name, int baseThreadIndex, int maximumYieldDepth) { -+ super(target, name); -+ this.baseThreadIndex = baseThreadIndex; -+ this.maximumYieldDepth = maximumYieldDepth; -+ } -+ -+ @Override -+ public boolean holdsYieldingLock() { -+ return this.heldYieldingLockCount > 0; -+ } -+ -+ @Override -+ public void incrementHeldYieldingLockCount() { -+ this.heldYieldingLockCount++; -+ if (this.heldYieldingLockCount == 1) { -+ this.updateCanStartYieldingTasks(); -+ } -+ } -+ -+ @Override -+ public void decrementHeldYieldingLockCount() { -+ this.heldYieldingLockCount--; -+ if (this.heldYieldingLockCount == 0) { -+ this.updateCanStartYieldingTasks(); -+ } -+ } -+ -+ /** -+ * Updates {@link #canStartYieldingTasks} according to {@link #yieldDepth} and {@link #heldYieldingLockCount}. -+ */ -+ private void updateCanStartYieldingTasks() { -+ this.canStartYieldingTasks = this.heldYieldingLockCount == 0 && this.yieldDepth < this.maximumYieldDepth; -+ } -+ -+ /** -+ * This method is based on {@link #signal}. -+ * {@link #signal} must always return true if this method returns true; -+ * otherwise {@link BaseThreadActivation} will get stuck while choosing a thread to activate. -+ * -+ * @see #signal -+ */ -+ @SuppressWarnings("RedundantIfStatement") -+ public boolean isWaitingAndNeedsSignal() { -+ if (this.isWaiting) { -+ if (this.isNotActuallyWaitingYet) { -+ if (!this.skipNextWait) { -+ return true; -+ } -+ return false; -+ } -+ if (!this.mayBeStillWaitingButHasBeenSignalled) { -+ return true; -+ } -+ } -+ return false; -+ } -+ -+ /** -+ * Yields to tasks: polls and executes tasks while possible and the stop condition is not met. -+ * The stop condition is met if {@code stopCondition} is not null and returns true, or alternatively, -+ * if {@code stopCondition} is null, and {@code yieldingLock} is successfully acquired. -+ * When no tasks can be polled, this thread will block, waiting for either a task that can be executed by this -+ * thread to become available, or for the {@code yieldingLock}, if given, to be released. -+ *
    -+ * Exactly one of {@code stopCondition} and {@code yieldingLock} must be non-null. -+ */ -+ public final void yieldUntil(@Nullable Long timeoutTime, @Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock) { -+ int oldYieldDepth = this.yieldDepth; -+ int newYieldDepth = oldYieldDepth + 1; -+ this.yieldDepth = newYieldDepth; -+ if (newYieldDepth == maximumYieldDepth) { -+ this.updateCanStartYieldingTasks(); -+ } -+ this.runTasksUntil(timeoutTime, stopCondition, yieldingLock); -+ this.yieldDepth = oldYieldDepth; -+ if (newYieldDepth == maximumYieldDepth) { -+ this.updateCanStartYieldingTasks(); -+ } -+ } -+ -+ /** -+ * This method will keep attempting to find a task to do, and execute it, and if none is found, start waiting -+ * until the {@code timeoutTime} is reached (which is compared to {@link System#nanoTime}), -+ * or the thread is signalled by {@link BaseThreadPool} or by a {@link YieldingLock}. -+ * The loop is broken as soon as the stop condition becomes true, or the given lock is successfully acquired. -+ *
    -+ * The above is the same as {@link #yieldUntil}, except it may be called in situations that is not 'yielding', -+ * for instance the endless loop polling tasks performed by a n{@link AssistThread}. The difference with -+ * {@link #yieldUntil} is that this method does not increment or decrement things the yield depth of this thread. -+ *
    -+ * Exactly one of {@code stopCondition} or {@code yieldingLock} must be non-null. -+ * -+ * @see #yieldUntil -+ */ -+ @ThisThreadOnly -+ @PotentiallyYielding("may yield further if an executed task is potentially yielding") -+ public final void runTasksUntil(@Nullable Long timeoutTime, @Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock) { -+ if (TickThread.isTickThread()) MinecraftServer.THREAD_DEBUG_LOGGER.ifPresent(it -> it.info("running tasks until")); -+ this.isPollingTaskOrCheckingStopCondition = true; -+ -+ /* -+ Endless loop that attempts to perform a task, and if one is found, tries to perform another again, -+ but if none is found, starts awaiting such a task to become available, or for the given yielding lock -+ to be released. -+ */ -+ while (true) { -+ try { -+ if (timeoutTime != null && System.nanoTime() - timeoutTime >= 0) { -+ break; -+ } -+ if (stopCondition != null) { -+ if (this == MinecraftServer.serverThread) { -+ MinecraftServer.currentManagedBlockStopConditionHasBecomeTrue = false; -+ } -+ if (stopCondition.getAsBoolean()) { -+ if (this == MinecraftServer.serverThread) { -+ MinecraftServer.currentManagedBlockStopConditionHasBecomeTrue = true; -+ } -+ break; -+ } -+ } else { -+ //noinspection ConstantConditions -+ if (yieldingLock.tryLock()) { -+ break; -+ } -+ } -+ } finally { -+ // Make sure other threads can be signalled for the last waited-for lock again -+ if (this.lastLockWaitedFor != null) { -+ this.lastLockWaitedFor.canBeSignalledFor = true; -+ this.lastLockWaitedFor = null; -+ } -+ } -+ -+ // If this is the original server thread, update isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking -+ if (this == MinecraftServer.serverThread) { -+ MinecraftServer.isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking = MinecraftServer.isInSpareTime && MinecraftServer.blockingCount == 0 && !MinecraftServer.SERVER.haveTime(); -+ } -+ -+ // Attempt to poll a task that can be started -+ Runnable task = this.pollTask(); -+ -+ // Run the task if found -+ if (task != null) { -+ -+ // If this is the server thread, potentially set nextTimeAssumeWeMayHaveDelayedTasks to true -+ if (this == MinecraftServer.serverThread && !MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks && AbstractTaskQueue.taskQueuesHaveTasks(BaseTaskQueueTier.SERVER.taskQueues)) { -+ MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks = true; -+ } -+ -+ // Update highestTierOfTaskOnStack and the thread priority -+ var highestTierBeforeTask = this.highestTierOfTaskOnStack; -+ var threadPriorityBeforeTask = this.getPriority(); -+ //noinspection DataFlowIssue -+ var newHighestTier = highestTierBeforeTask == null ? this.lastPolledTaskTier : highestTierBeforeTask.ordinal < this.lastPolledTaskTier.ordinal ? highestTierBeforeTask : this.lastPolledTaskTier; -+ //noinspection DataFlowIssue -+ var newThreadPriority = newHighestTier.threadPriority; -+ if (newHighestTier != highestTierBeforeTask) { -+ this.highestTierOfTaskOnStack = newHighestTier; -+ BaseThreadActivation.callForUpdate(); -+ if (threadPriorityBeforeTask != newThreadPriority) { -+ this.setPriority(newThreadPriority); -+ } -+ } -+ -+ this.isPollingTaskOrCheckingStopCondition = false; -+ task.run(); -+ -+ // If this is the server thread, execute some chunk tasks -+ if (this == MinecraftServer.serverThread) { -+ if (newHighestTier != BaseTaskQueueTier.SERVER) { -+ newHighestTier = BaseTaskQueueTier.SERVER; -+ this.highestTierOfTaskOnStack = newHighestTier; -+ BaseThreadActivation.callForUpdate(); -+ if (newThreadPriority != newHighestTier.threadPriority) { -+ newThreadPriority = newHighestTier.threadPriority; -+ this.setPriority(newThreadPriority); -+ } -+ } -+ MinecraftServer.SERVER.executeMidTickTasks(); // Paper - execute chunk tasks mid tick -+ } -+ -+ // Reset highestTierOfTaskOnStack and the thread priority -+ if (newHighestTier != highestTierBeforeTask) { -+ this.highestTierOfTaskOnStack = highestTierBeforeTask; -+ BaseThreadActivation.callForUpdate(); -+ if (threadPriorityBeforeTask != newThreadPriority) { -+ this.setPriority(threadPriorityBeforeTask); -+ } -+ } -+ -+ this.isPollingTaskOrCheckingStopCondition = true; -+ continue; -+ -+ } -+ -+ /* -+ If no task that can be started by this thread was found, wait for a task that we are allowed -+ to poll to become available (when that happens, the BaseThreadPool will signal this thread), -+ or for the given yielding lock to be released. This is the only time we should ever block inside -+ a potentially yielding procedure. -+ */ -+ this.waitUntilSignalled(timeoutTime, yieldingLock); -+ -+ } -+ -+ this.isPollingTaskOrCheckingStopCondition = false; -+ -+ /* -+ If the thread was signalled for another reason than the lock, but we acquired the lock instead, -+ another thread should be signalled for that reason. -+ */ -+ SignalReason lastSignalReason = this.lastSignalReason; -+ if (lastSignalReason != null && yieldingLock != null && lastSignalReason != SignalReason.YIELDING_LOCK) { -+ BaseThreadActivation.callForUpdate(); -+ } -+ -+ } -+ -+ /** -+ * @see #pollTask() -+ */ -+ @ThisThreadOnly -+ @YieldFree -+ private @Nullable Runnable pollTaskFromTier(BaseTaskQueueTier tier, boolean tinyOnly) { -+ for (var queue : tier.taskQueues) { -+ // Check whether we can not yield to the queue, if we are yielding -+ boolean canQueueBeYieldedTo = queue.canBeYieldedTo(); -+ if (!canQueueBeYieldedTo && this.yieldDepth > 0) { -+ continue; -+ } -+ Runnable task = tinyOnly ? queue.pollTiny(this) : queue.poll(this); -+ if (task != null) { -+ this.lastPolledTaskTier = tier; -+ return task; -+ } -+ /* -+ Check if the tier has run out of tasks for a span, -+ in order to update BaseThreadActivation#thereMayBeTasks. -+ */ -+ for (int spanI = 0; spanI < TaskSpan.length; spanI++) { -+ TaskSpan span = TaskSpan.VALUES[spanI]; -+ if (queue.canHaveTasks(span)) { -+ int oldTasks = BaseThreadActivation.thereMayBeTasks[tier.ordinal][spanI][canQueueBeYieldedTo ? 1 : 0].get(); -+ if (oldTasks > 0) { -+ if (!queue.hasTasks(span)) { -+ boolean tierHasNoTasksForSpan = true; -+ for (AbstractTaskQueue otherTierQueue : tier.taskQueues) { -+ // We already know there are no tasks in this queue -+ if (otherTierQueue == queue) { -+ continue; -+ } -+ if (otherTierQueue.hasTasks(span)) { -+ tierHasNoTasksForSpan = false; -+ break; -+ } -+ } -+ if (tierHasNoTasksForSpan) { -+ // Set thereMayBeTasks to false, but only if it did not change in the meantime -+ BaseThreadActivation.thereMayBeTasks[tier.ordinal][spanI][canQueueBeYieldedTo ? 1 : 0].compareAndSet(oldTasks, 0); -+ } -+ } -+ } -+ } -+ } -+ } -+ return null; -+ } -+ -+ /** -+ * Polls a task from any queue this thread can currently poll from, and returns it. -+ * Polling potentially yielding tasks is attempted before yield-free tasks. -+ * -+ * @return The task that was polled, or null if no task was found. -+ */ -+ @ThisThreadOnly -+ @YieldFree -+ private @Nullable Runnable pollTask() { -+ /* -+ * If this is a server thread, poll from SERVER, and poll tiny tasks from other tiers. -+ * Note that when polling on the ServerThread, we do not check whether we would be allowed to do so -+ * by the BaseThreadPool, as we consider keeping the ServerThread in the Thread.State.RUNNABLE state for -+ * as long as possible to be more important than the off-chance of for example starting a TINY ASYNC task -+ * on the server thread while no ASYNC tasks are allowed to be polled by other threads at the moment. -+ */ -+ if (this instanceof ServerThread) { -+ // Poll from the SERVER queues -+ Runnable task = this.pollTaskFromTier(BaseTaskQueueTier.SERVER, false); -+ if (task != null) { -+ return task; -+ } -+ // Poll tiny tasks from other tiers -+ for (var tier : BaseTaskQueueTier.VALUES_EXCEPT_SERVER) { -+ task = this.pollTaskFromTier(tier, true); -+ if (task != null) { -+ return task; -+ } -+ } -+ // We failed to poll any task -+ return null; -+ } -+ // If this is not a server thread, poll from all queues except SERVER -+ for (var tier : BaseTaskQueueTier.VALUES_EXCEPT_SERVER) { -+ /* -+ Make sure that we are allowed to poll from the tier, according to the presence of an excess number of -+ threads working on tasks from that tier during the last BaseThreadActivation#update call. -+ In the case this check's result is too optimistic, and a task is started when ideally it wouldn't have been, -+ then so be it - it is not terrible. Whenever this happens, enough threads will surely be allocated -+ by the BaseThreadPool for the task tier that is more in demand anyway, so it does not matter much. -+ In the case this check's result is too pessimistic, the polling fails and this thread will start to sleep, -+ but before doing this, will make a call to BaseThreadActivation#callForUpdate that re-activated this -+ thread if necessary, so no harm is done. -+ In the case this check causes this thread to go to sleep, the call to BaseThreadActivation#callForUpdate -+ while isWaiting is true will make sure the BaseThreadPool has the ability to correctly activate a -+ different thread (that is able to start tasks of a higher tier) if needed. -+ Here, we do not even make an exception for TINY tasks, since there may already be ongoing avoidable -+ context-switching due to excess threads that we can solve by letting this thread go to sleep. -+ */ -+ if (tier.ordinal < BaseThreadActivation.tierInExcessOrdinal) { -+ /* -+ Tasks of a certain tier may yield to tasks of the same or a higher -+ tier, and they may also yield to tiny tasks of a lower tier. -+ */ -+ var tierYieldingFrom = this.highestTierOfTaskOnStack; -+ Runnable task = this.pollTaskFromTier(tier, tierYieldingFrom != null && tier.ordinal > tierYieldingFrom.ordinal); -+ if (task != null) { -+ return task; -+ } -+ } -+ } -+ // We failed to poll any task -+ return null; -+ } -+ -+ /** -+ * Starts waiting on something to do. -+ * -+ * @param timeoutTime The maximum time to wait until (compared to {@link System#nanoTime}). -+ * @param yieldingLock A {@link YieldingLock} to register with, or null if this thread is not waiting for -+ * a yielding lock. -+ */ -+ @ThisThreadOnly -+ @PotentiallyBlocking -+ private void waitUntilSignalled(@Nullable Long timeoutTime, @Nullable YieldingLock yieldingLock) { -+ -+ // Remember whether we registered to wait with the lock, to unregister later -+ // Register this thread with the lock if necessary -+ boolean registeredAsWaitingWithLock = false; -+ if (yieldingLock != null) { -+ // No point in registering if we're not going to wait anyway -+ if (!this.skipNextWait) { -+ yieldingLock.incrementWaitingThreads(); -+ registeredAsWaitingWithLock = true; -+ } -+ } -+ -+ /* -+ Remember whether we changed anything that requires a BaseThreadPool#update call -+ (after the last call to that method). -+ */ -+ boolean mustCallPoolUpdateAtEnd = false; -+ -+ /* -+ If we cannot acquire the lock, we can assume this thread is being signalled, -+ so there is no reason to start waiting. -+ */ -+ waitWithLock: if (this.waitLock.tryLock()) { -+ try { -+ -+ // If it was set that this thread should skip the wait in the meantime, skip it -+ if (this.skipNextWait) { -+ break waitWithLock; -+ } -+ -+ // Mark this thread as waiting -+ this.lockWaitingFor = yieldingLock; -+ this.mayBeStillWaitingButHasBeenSignalled = false; -+ this.isWaiting = true; -+ // But actually we are not waiting yet, signal has no effect yet during the next short lock release -+ this.isNotActuallyWaitingYet = true; -+ -+ } finally { -+ this.waitLock.unlock(); -+ } -+ -+ // Update the pool -+ BaseThreadActivation.callForUpdate(); -+ -+ /* -+ If we cannot acquire the lock, we can assume this thread is being signalled, -+ so there is no reason to start waiting. -+ */ -+ if (this.waitLock.tryLock()) { -+ try { -+ -+ // We passed the short lock release -+ this.isNotActuallyWaitingYet = false; -+ -+ // If it was set that this thread should skip the wait in the meantime, skip it -+ if (this.skipNextWait) { -+ this.isWaiting = false; -+ this.lastLockWaitedFor = this.lockWaitingFor; -+ this.lockWaitingFor = null; -+ mustCallPoolUpdateAtEnd = true; -+ break waitWithLock; -+ } -+ -+ // Wait -+ try { -+ -+ // -1 indicates to not use a timeout (this value is not later set to any other negative value) -+ long waitForNanos = -1; -+ if (timeoutTime != null) { -+ waitForNanos = Math.max(timeoutTime - System.nanoTime(), SERVER_THREAD_WAIT_NANOS_MINIMUM); -+ } else { -+ /* -+ Check if we should wait with a tick-based timeout: -+ this only happens if this thread is the server thread, in -+ which case we do not want to wait past the start of the next tick. -+ */ -+ if (this == MinecraftServer.serverThread) { -+ if (MinecraftServer.isWaitingUntilNextTick) { -+ /* -+ During waiting until the next tick, we wait until the next tick start. -+ If it already passed, we do not have to use a timeout, because we will be notified -+ when the stop condition becomes true. -+ */ -+ waitForNanos = MinecraftServer.nextTickStartNanoTime - System.nanoTime(); -+ if (waitForNanos < 0) { -+ waitForNanos = -1; -+ } -+ } else if (MinecraftServer.SERVER.isOversleep) { -+ /* -+ During this phase, MinecraftServer#mayHaveDelayedTasks() is checked, and we may not -+ be notified when it changes. Therefore, if the next tick start has not passed, we will -+ wait until then, but if it has, we wait for a short interval to make sure we keep -+ checking the stop condition (but not for longer than until the last time we can be -+ executing extra delayed tasks). -+ */ -+ waitForNanos = MinecraftServer.nextTickStartNanoTime - System.nanoTime(); -+ if (waitForNanos < 0) { -+ waitForNanos = Math.min(Math.max(0, MinecraftServer.delayedTasksMaxNextTickNanoTime - System.nanoTime()), SERVER_THREAD_WAIT_NANOS_DURING_OVERSLEEP_WITH_DELAYED_TASKS); -+ } -+ } -+ } -+ } -+ if (waitForNanos >= 0) { -+ // Set the last signal reason to null in case the timeout elapses without a signal -+ this.lastSignalReason = null; -+ // Skip if the time is too short -+ if (waitForNanos >= SERVER_THREAD_WAIT_NANOS_MINIMUM) { -+ //noinspection ResultOfMethodCallIgnored -+ this.waitCondition.await(waitForNanos, TimeUnit.NANOSECONDS); -+ } -+ } else { -+ /* -+ If we did not wait with a timeout, wait indefinitely. If this thread is the server thread, -+ and the intended start time of the next tick has already passed, but the stop condition to stop -+ running tasks is still not true, this thread must be signalled when a change in conditions causes -+ the stop condition to become true. -+ */ -+ this.waitCondition.await(); -+ } -+ -+ } catch (InterruptedException e) { -+ throw new IllegalStateException(e); -+ } -+ -+ // Unmark this thread as waiting -+ this.isWaiting = false; -+ this.lastLockWaitedFor = this.lockWaitingFor; -+ this.lockWaitingFor = null; -+ mustCallPoolUpdateAtEnd = true; -+ -+ } finally { -+ this.waitLock.unlock(); -+ } -+ } -+ -+ } -+ -+ // Unregister this thread from the lock if necessary -+ if (registeredAsWaitingWithLock) { -+ yieldingLock.decrementWaitingThreads(); -+ } -+ -+ // Reset skipping the next wait -+ this.skipNextWait = false; -+ -+ // Update the pool if necessary -+ if (mustCallPoolUpdateAtEnd) { -+ BaseThreadActivation.callForUpdate(); -+ } -+ -+ } -+ -+ /** -+ * An auxiliary method for exclusive use in {@link #signal}, that marks the {@link YieldingLock} -+ * that this thread is waiting for as having been signalled for, so that no other threads -+ * are also signalled for it. -+ *
    -+ * This must be called when {@link #signal} returns true, and must be called before any other -+ * actions relating to the signalling of this thread are performed. -+ */ -+ private void markLockWaitingForAsSignalledFor() { -+ @Nullable YieldingLock lockWaitingFor = this.lockWaitingFor; -+ if (lockWaitingFor != null) { -+ lockWaitingFor.canBeSignalledFor = false; -+ } -+ } -+ -+ /** -+ * Signals this thread to wake up, or if it was not sleeping but attempting to poll a task: -+ * to not go to sleep the next time no task could be polled, and instead try polling a task again. -+ * -+ * @param reason The reason why this thread was signalled, or null if it is irrelevant (e.g. when the signal -+ * will never need to be repeated because there is only thread waiting for this specific event -+ * to happen). -+ * @return Whether this thread was sleeping before, and has woken up now, -+ * or whether {@link #skipNextWait} was set to true. -+ */ -+ @AnyThreadSafe -+ @YieldFree -+ public final boolean signal(@Nullable SignalReason reason) { -+ while (!this.waitLock.tryLock()) { // TODO Gale use a wait-free system here by using a sort of leave-a-message-at-the-door Atomic class system -+ Thread.onSpinWait(); -+ } -+ try { -+ if (this.isWaiting) { -+ if (this.isNotActuallyWaitingYet) { -+ if (!this.skipNextWait) { -+ this.markLockWaitingForAsSignalledFor(); -+ this.lastSignalReason = reason; -+ this.skipNextWait = true; -+ return true; -+ } -+ return false; -+ } -+ if (!this.mayBeStillWaitingButHasBeenSignalled) { -+ this.markLockWaitingForAsSignalledFor(); -+ this.lastSignalReason = reason; -+ this.mayBeStillWaitingButHasBeenSignalled = true; -+ this.waitCondition.signal(); -+ return true; -+ } -+ } else if (this.isPollingTaskOrCheckingStopCondition) { -+ if (!this.skipNextWait) { -+ this.markLockWaitingForAsSignalledFor(); -+ this.lastSignalReason = reason; -+ this.skipNextWait = true; -+ return true; -+ } -+ } -+ return false; -+ } finally { -+ this.waitLock.unlock(); -+ } -+ } -+ -+ /** -+ * @return The current thread if it is a {@link BaseThread}, or null otherwise. -+ */ -+ @SuppressWarnings("unused") -+ @AnyThreadSafe -+ @YieldFree -+ public static @Nullable BaseThread currentBaseThread() { -+ return Thread.currentThread() instanceof BaseThread baseThread ? baseThread : null; -+ } -+ -+ /** -+ * @return Whether the current thread is a {@link BaseThread}. -+ */ -+ @SuppressWarnings("unused") -+ @AnyThreadSafe -+ @YieldFree -+ public static boolean isBaseThread() { -+ return Thread.currentThread() instanceof BaseThread; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/thread/OriginalServerThread.java b/src/main/java/org/galemc/gale/executor/thread/OriginalServerThread.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ced372b40e8b3a5c43dabf5bb547a71e3c713d2f ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/thread/OriginalServerThread.java -@@ -0,0 +1,20 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.thread; -+ -+import net.minecraft.server.MinecraftServer; -+import org.spigotmc.WatchdogThread; -+ -+/** -+ * A type that is unique to {@link MinecraftServer#serverThread}, -+ * to distinguish it from {@link WatchdogThread#instance}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public final class OriginalServerThread extends ServerThread { -+ -+ public OriginalServerThread(final Runnable run, final String name) { -+ super(run, name); -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/thread/ServerThread.java b/src/main/java/org/galemc/gale/executor/thread/ServerThread.java -new file mode 100644 -index 0000000000000000000000000000000000000000..7d58d995d8e74cd5f51f85f123166bf884deed92 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/thread/ServerThread.java -@@ -0,0 +1,51 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.thread; -+ -+import io.papermc.paper.util.TickThread; -+import net.minecraft.server.MinecraftServer; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+import org.spigotmc.WatchdogThread; -+ -+/** -+ * A {@link TickThread} that provides an implementation for {@link BaseThread}, -+ * that is shared between the {@link MinecraftServer#serverThread} and {@link WatchdogThread#instance}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public class ServerThread extends TickThread { -+ -+ protected ServerThread(final String name) { -+ super(name); -+ } -+ -+ protected ServerThread(final Runnable run, final String name) { -+ super(run, name); -+ } -+ -+ /** -+ * This method must not be called while {@link MinecraftServer#isConstructed} is false. -+ * -+ * @return The global {@link ServerThread} instance, which is either -+ * {@link MinecraftServer#serverThread}, or {@link WatchdogThread#instance} while the server is shutting -+ * down and the {@link WatchdogThread} was responsible. -+ */ -+ public static @NotNull ServerThread getInstance() { -+ if (MinecraftServer.SERVER.hasStopped) { -+ if (MinecraftServer.SERVER.shutdownThread == WatchdogThread.instance) { -+ return WatchdogThread.instance; -+ } -+ } -+ return MinecraftServer.serverThread; -+ } -+ -+ /** -+ * @return The same value as {@link #getInstance()} if {@link MinecraftServer#isConstructed} is true, -+ * or null otherwise. -+ */ -+ public static @Nullable ServerThread getInstanceIfConstructed() { -+ return MinecraftServer.isConstructed ? getInstance() : null; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/thread/SignallableThread.java b/src/main/java/org/galemc/gale/executor/thread/SignallableThread.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d9b2a445dd320979741fd659b1692eeec3c841f8 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/thread/SignallableThread.java -@@ -0,0 +1,32 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.thread; -+ -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.thread.platform.SignalReason; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * An interface for threads that can wait (either by blocking or yielding) for events, and be signalled when -+ * circumstances may have changed. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public interface SignallableThread { -+ -+ /** -+ * Signals this thread to wake up, or if it was not sleeping but attempting to poll a task: -+ * to not go to sleep the next time no task could be polled, and instead try polling a task again. -+ * -+ * @param reason The reason why this thread was signalled, or null if it is irrelevant (e.g. when the signal -+ * will never need to be repeated because there is only thread waiting for this specific event -+ * to happen). -+ * @return Whether this thread was sleeping before, and had not been signalled to wake up before, -+ * but has or will be woken up due to this signal. -+ */ -+ @AnyThreadSafe -+ @YieldFree -+ boolean signal(@Nullable SignalReason reason); -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/thread/deferral/ServerThreadDeferral.java b/src/main/java/org/galemc/gale/executor/thread/deferral/ServerThreadDeferral.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8c4855c931ccc285768eabcff9d1f2b752d45bf6 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/thread/deferral/ServerThreadDeferral.java -@@ -0,0 +1,151 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.thread.deferral; -+ -+import io.papermc.paper.util.TickThread; -+import org.galemc.gale.concurrent.UnterminableExecutorService; -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.queue.BaseTaskQueues; -+import org.galemc.gale.executor.thread.AbstractYieldingThread; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.ServerThread; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.Executor; -+import java.util.concurrent.ExecutorService; -+import java.util.function.Supplier; -+ -+/** -+ * This class provides functionality to allow any thread, -+ * including but not limited to a {@link BaseThread}, -+ * to defer blocks of code to a {@link ServerThread}, and wait for its completion. -+ *
    -+ * Using deferral from a {@link TickThread} that is not the correct thread already is highly discouraged -+ * because yielding from a {@link TickThread} should be avoided whenever possible. -+ * -+ * @see TickThreadDeferral -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@SuppressWarnings("unused") -+public final class ServerThreadDeferral { -+ -+ private ServerThreadDeferral() {} -+ -+ /** -+ * @see #defer(Supplier, TaskSpan) -+ */ -+ public static void defer(Runnable task, TaskSpan span) { -+ deferInternal(task, null, span); -+ } -+ -+ /** -+ * Defers the given {@code task} to a {@link ServerThread}, and yields until it has finished. -+ * If this thread is a {@link ServerThread}, the task will be executed right away. -+ *
    -+ * The task itself must be non-blocking and may be potentially yielding, but keeping the task yield-free is -+ * highly preferred because during yielding from a {@link ServerThread}, most other tasks that must be -+ * executed on a {@link ServerThread} cannot be run. -+ *
    -+ * On an {@link AbstractYieldingThread}, this method yields until the task is completed. -+ * Like any potentially yielding method, while technically possible to call from any thread, this method should -+ * generally only be called from a yielding thread, because on any other thread, the thread will block until -+ * the given task has been completed by the main thread. -+ *
    -+ * If this thread is already an appropriate thread to run the task on, the task is performed on this thread. -+ * -+ * @param task The task to run. -+ * @param span The {@link TaskSpan} of the task. -+ */ -+ public static T defer(Supplier task, TaskSpan span) { -+ return deferInternal(null, task, span); -+ } -+ -+ /** -+ * Common implementation for {@link #defer(Runnable, TaskSpan)} and {@link #defer(Supplier, TaskSpan)}. -+ * Exactly one of {@code runnable} or {@code supplier} must be non-null. -+ */ -+ private static T deferInternal(@Nullable Runnable runnable, @Nullable Supplier supplier, TaskSpan span) { -+ // Check if we are the right thread -+ if (TickThread.isTickThread()) { -+ if (runnable == null) { -+ //noinspection ConstantConditions -+ return supplier.get(); -+ } -+ runnable.run(); -+ return null; -+ } -+ // Otherwise, schedule the task and wait for it to complete -+ CompletableFuture future = new CompletableFuture<>(); -+ AbstractYieldingThread yieldingThread = AbstractYieldingThread.currentYieldingThread(); -+ if (yieldingThread != null) { -+ // Yield until the task completes -+ BaseTaskQueues.deferredToServerThread.add(() -> { -+ if (runnable == null) { -+ //noinspection ConstantConditions -+ future.complete(supplier.get()); -+ } else { -+ runnable.run(); -+ future.complete(null); -+ } -+ yieldingThread.signal(null); -+ }, span); -+ yieldingThread.yieldUntil(null, future::isDone, null); -+ return future.getNow(null); -+ } else { -+ // Block until the task completes -+ BaseTaskQueues.deferredToServerThread.add(() -> { -+ if (runnable == null) { -+ //noinspection ConstantConditions -+ future.complete(supplier.get()); -+ } else { -+ runnable.run(); -+ future.complete(null); -+ } -+ }, span); -+ return future.join(); -+ } -+ } -+ -+ /** -+ * An executor for deferring {@link TaskSpan#YIELDING} tasks to the main thread, -+ * where {@link Executor#execute} calls {@link #defer}. -+ */ -+ public static final ExecutorService yieldingExecutor = new UnterminableExecutorService() { -+ -+ @Override -+ public void execute(@NotNull Runnable runnable) { -+ defer(runnable, TaskSpan.YIELDING); -+ } -+ -+ }; -+ -+ /** -+ * An executor for deferring {@link TaskSpan#FREE} tasks to the main thread, -+ * where {@link Executor#execute} calls {@link #defer}. -+ */ -+ public static final ExecutorService freeExecutor = new UnterminableExecutorService() { -+ -+ @Override -+ public void execute(@NotNull Runnable runnable) { -+ defer(runnable, TaskSpan.FREE); -+ } -+ -+ }; -+ -+ /** -+ * An executor for deferring {@link TaskSpan#TINY} tasks to the main thread, -+ * where {@link Executor#execute} calls {@link #defer}. -+ */ -+ public static final ExecutorService tinyExecutor = new UnterminableExecutorService() { -+ -+ @Override -+ public void execute(@NotNull Runnable runnable) { -+ defer(runnable, TaskSpan.TINY); -+ } -+ -+ }; -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/thread/deferral/TickThreadDeferral.java b/src/main/java/org/galemc/gale/executor/thread/deferral/TickThreadDeferral.java -new file mode 100644 -index 0000000000000000000000000000000000000000..77fe10e51b00115da520cfc211bf84badbc027be ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/thread/deferral/TickThreadDeferral.java -@@ -0,0 +1,159 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.thread.deferral; -+ -+import io.papermc.paper.util.TickThread; -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.world.entity.Entity; -+import org.galemc.gale.concurrent.UnterminableExecutorService; -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.thread.AbstractYieldingThread; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.queue.BaseTaskQueueTier; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.concurrent.Executor; -+import java.util.concurrent.ExecutorService; -+import java.util.function.Supplier; -+ -+/** -+ * This class provides functionality to allow any thread, -+ * including but not limited to a {@link BaseThread}, -+ * to defer blocks of code to a {@link TickThread}, and wait for its completion. -+ * In other words, instead of the typical paradigm where a block of code is executed -+ * while a lock is held by the thread, we do not acquire a lock, but instead schedule the code -+ * to be run on a thread responsible for the specific aspects of the code -+ * (thereby avoiding deadlocks caused by the acquisition of multiple locks in various orders, -+ * and avoiding collisions between parts of code that can not run concurrently, -+ * which occur especially easy in parts of code that may have to call callbacks of which -+ * we can only make limited assumptions) and wait for that to finish. -+ *
    -+ * This has a number of advantages. -+ * When we require running code that checks whether it is being run on an appropriate {@link TickThread}, -+ * we can run it this way. Since these parts of code are always performed on a {@link TickThread} -+ * regardless of the thread requesting them to be run, there is no chance of deadlock occurring -+ * from two different locks being desired in a different order on two of the original threads -+ * (in fact, if the normally guarded blocks of code are always run exclusively to each other -+ * when deferred this way, we do not need locks at all). -+ *
    -+ * When deferring from an {@link AbstractYieldingThread}, -+ * we yield to other tasks until the deferred block of code has finished. -+ * When deferring from another type of thread, the thread is blocked. -+ *
    -+ * Using deferral from a {@link TickThread} that is not the correct thread already is highly discouraged -+ * because yielding from a {@link TickThread} should be avoided whenever possible. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@SuppressWarnings("unused") -+public final class TickThreadDeferral { -+ -+ private TickThreadDeferral() {} -+ -+ /** -+ * This may be useful in the future. See the documentation of {@link BaseTaskQueueTier#SERVER}. -+ * -+ * @see #defer(Runnable, TaskSpan) -+ */ -+ public static void defer(final ServerLevel world, final int chunkX, final int chunkZ, Runnable task, TaskSpan span) { -+ defer(task, span); -+ } -+ -+ /** -+ * This may be useful in the future. See the documentation of {@link BaseTaskQueueTier#SERVER}. -+ * -+ * @see #defer(Supplier, TaskSpan) -+ */ -+ public static T defer(final ServerLevel world, final int chunkX, final int chunkZ, Supplier task, TaskSpan span) { -+ return defer(task, span); -+ } -+ -+ /** -+ * This may be useful in the future. See the documentation of {@link BaseTaskQueueTier#SERVER}. -+ * -+ * @see #defer(Runnable, TaskSpan) -+ */ -+ public static void defer(final Entity entity, Runnable task, TaskSpan span) { -+ defer(task, span); -+ } -+ -+ /** -+ * This may be useful in the future. See the documentation of {@link BaseTaskQueueTier#SERVER}. -+ * -+ * @see #defer(Supplier, TaskSpan) -+ */ -+ public static T defer(final Entity entity, Supplier task, TaskSpan span) { -+ return defer(task, span); -+ } -+ -+ /** -+ * @see #defer(Supplier, TaskSpan) -+ */ -+ public static void defer(Runnable task, TaskSpan span) { -+ // Current implementation uses ServerThreadDeferral -+ ServerThreadDeferral.defer(task, span); -+ } -+ -+ /** -+ * Defers the given {@code task} to any {@link TickThread}, and yields until it has finished. -+ * If this thread is a {@link TickThread}, the task will be executed right away. -+ *
    -+ * The task itself must be non-blocking and may be potentially yielding, but keeping the task yield-free is -+ * highly preferred because during yielding from a {@link TickThread}, other tasks that must be executed on that -+ * thread cannot be run. -+ *
    -+ * On a {@link AbstractYieldingThread}, this method yields until the task is completed. -+ * Like any potentially yielding method, while technically possible to call from any thread, this method should -+ * generally only be called from a yielding thread, because on any other thread, the thread will block until -+ * the given task has been completed by the main thread. -+ *
    -+ * If this thread is already an appropriate thread to run the task on, the task is performed on this thread. -+ * -+ * @param task The task to run. -+ * @param span The {@link TaskSpan} of the task. -+ */ -+ public static T defer(Supplier task, TaskSpan span) { -+ // Current implementation uses ServerThreadDeferral -+ return ServerThreadDeferral.defer(task, span); -+ } -+ -+ /** -+ * An executor for deferring {@link TaskSpan#YIELDING} tasks to the main thread, -+ * where {@link Executor#execute} calls {@link #defer}. -+ */ -+ public static final ExecutorService yieldingExecutor = new UnterminableExecutorService() { -+ -+ @Override -+ public void execute(@NotNull Runnable runnable) { -+ defer(runnable, TaskSpan.YIELDING); -+ } -+ -+ }; -+ -+ /** -+ * An executor for deferring {@link TaskSpan#FREE} tasks to the main thread, -+ * where {@link Executor#execute} calls {@link #defer}. -+ */ -+ public static final ExecutorService freeExecutor = new UnterminableExecutorService() { -+ -+ @Override -+ public void execute(@NotNull Runnable runnable) { -+ defer(runnable, TaskSpan.FREE); -+ } -+ -+ }; -+ -+ /** -+ * An executor for deferring {@link TaskSpan#TINY} tasks to the main thread, -+ * where {@link Executor#execute} calls {@link #defer}. -+ */ -+ public static final ExecutorService tinyExecutor = new UnterminableExecutorService() { -+ -+ @Override -+ public void execute(@NotNull Runnable runnable) { -+ defer(runnable, TaskSpan.TINY); -+ } -+ -+ }; -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/thread/platform/SignalReason.java b/src/main/java/org/galemc/gale/executor/thread/platform/SignalReason.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c41824669e66f719d37ab5539fe6ab7f994a003f ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/thread/platform/SignalReason.java -@@ -0,0 +1,24 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.thread.platform; -+ -+import org.galemc.gale.executor.lock.YieldingLock; -+import org.galemc.gale.executor.thread.SignallableThread; -+ -+/** -+ * A reason of a call to {@link SignallableThread#signal}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public enum SignalReason { -+ -+ /** -+ * A task that the signalled thread could poll and start is available. -+ */ -+ TASK, -+ /** -+ * The {@link YieldingLock} that the signalled thread was waiting for was released. -+ */ -+ YIELDING_LOCK -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadActivation.java b/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadActivation.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ef6dc61f22ef4ab28da3213163fbc8b5479cfc16 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadActivation.java -@@ -0,0 +1,659 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.thread.pool; -+ -+import net.minecraft.server.MinecraftServer; -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.lock.YieldingLock; -+import org.galemc.gale.executor.queue.AbstractTaskQueue; -+import org.galemc.gale.executor.queue.BaseTaskQueueTier; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.ServerThread; -+import org.galemc.gale.executor.thread.platform.SignalReason; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.ArrayList; -+import java.util.Arrays; -+import java.util.List; -+import java.util.concurrent.atomic.AtomicBoolean; -+import java.util.concurrent.atomic.AtomicInteger; -+import java.util.concurrent.atomic.AtomicLong; -+import java.util.concurrent.atomic.AtomicReference; -+ -+/** -+ * A class providing the static functionality needed to activate more threads in the {@link BaseThreadPool} -+ * when needed. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public final class BaseThreadActivation { -+ -+ private BaseThreadActivation() {} -+ -+ /** -+ * The delay in nanoseconds that is applied to {@link System#nanoTime()} -+ * when computing {@link #nextAllowedFrequentSignalNewTasksTime}. -+ */ -+ public static final long FREQUENT_SIGNAL_NEW_TASKS_INTERVAL = 100_000; -+ -+ /** -+ * The last time {@link #newTaskWasAdded}'s content was actually run. -+ * This value is useful to limit the number of runs of the method by potential frequent callers, -+ * such as the chunk task executors. -+ */ -+ private static final AtomicLong nextAllowedFrequentSignalNewTasksTime = new AtomicLong(System.nanoTime() - 1L); -+ -+ /** -+ * This value is not null while an update is ongoing. -+ * -+ * @see #update() -+ */ -+ static final AtomicReference updateOngoingOnThread = new AtomicReference(); -+ -+ /** -+ * Whether a non-{@link ServerThread} thread is ready to take over the {@link #update} call -+ * that is ongoing on a {@link ServerThread}. -+ * -+ * @see #callForUpdate() -+ */ -+ private static final AtomicBoolean isNonServerThreadReadyToTakeOverUpdate = new AtomicBoolean(); -+ -+ /** -+ * @see #update() -+ */ -+ private static final AtomicInteger newUpdateCallsReceived = new AtomicInteger(); -+ -+ /** -+ * A re-usable array for use inside {@link #update()}. -+ */ -+ private static final int[] numberOfThreadsActiveForTier = new int[BaseTaskQueueTier.length]; -+ -+ /** -+ * A re-usable array for use inside {@link #update()}. -+ */ -+ @SuppressWarnings("unchecked") -+ private static final List[] threadsWaitingForUnlockedLockForTier = new List[BaseTaskQueueTier.length]; -+ static { -+ for (int tierI = 0; tierI < BaseTaskQueueTier.length; tierI++) { -+ threadsWaitingForUnlockedLockForTier[tierI] = new ArrayList<>(); -+ } -+ } -+ -+ /** -+ * A re-usable array for use inside {@link #update()}. -+ */ -+ private static final int[] numberOfThreadsActiveForLowerThanTier = new int[BaseTaskQueueTier.length]; -+ -+ /** -+ * A re-usable array for use inside {@link #update()}. -+ */ -+ private static final int[] numberOfThreadsActiveForHigherThanTier = new int[BaseTaskQueueTier.length]; -+ -+ /** -+ * A re-usable array for use inside {@link #update()}. -+ */ -+ private static final int[] numberOfThreadsIntendedToBeActiveForTier = new int[BaseTaskQueueTier.length]; -+ -+ /** -+ * An array indicating, per {@link BaseTaskQueueTier} (indexed by their {@link BaseTaskQueueTier#ordinal}) -+ * per {@link TaskSpan} (indexed by their {@link TaskSpan#ordinal}) per whether for queues that allow being -+ * yielded to ({@link AbstractTaskQueue#canBeYieldedTo()}) (1) or not (0), whether there may be tasks -+ * for that tier and span, indicated by whether the value is positive (indicating true) or 0 (indicating false). -+ * It is always incremented before calling {@link #update()} due to new tasks being added. -+ * If it is 0, it is certain that either there are no queued task for the tier, or -+ * a task has just been added to the queue and this value has not yet been set to true, but will be due -+ * to a {@link #newTaskWasAdded} call, which is then followed by a {@link #callForUpdate()} call. -+ */ -+ public static final AtomicInteger[][][] thereMayBeTasks = new AtomicInteger[BaseTaskQueueTier.length][TaskSpan.length][2]; -+ static { -+ for (int tierI = 0; tierI < BaseTaskQueueTier.length; tierI++) { -+ for (int spanI = 0; spanI < TaskSpan.length; spanI++) { -+ for (int canBeYieldedTo = 0; canBeYieldedTo <= 1; canBeYieldedTo++) { -+ thereMayBeTasks[tierI][spanI][canBeYieldedTo] = new AtomicInteger(); -+ } -+ } -+ } -+ } -+ -+ /** -+ * The {@link BaseTaskQueueTier#ordinal} of the highest tier (which means the lowest -+ * {@link BaseTaskQueueTier#ordinal}) for which the number of present threads -+ * have been determined by the last call to {@link #update()} to be in excess. This value is -+ * {@link BaseTaskQueueTier#length} when no threads are in excess. -+ */ -+ public static volatile int tierInExcessOrdinal = BaseTaskQueueTier.length; -+ -+ private static long updateNextAllowedFrequentSignalNewTasksTime(long value) { -+ long newValue = System.nanoTime() + FREQUENT_SIGNAL_NEW_TASKS_INTERVAL; -+ return newValue - value >= 0 ? newValue : value; -+ } -+ -+ /** -+ * @see #newTaskWasAdded(BaseTaskQueueTier, TaskSpan, boolean, boolean) -+ */ -+ public static void newTaskWasAdded(BaseTaskQueueTier tier, TaskSpan span) { -+ newTaskWasAdded(tier, span, true, false); -+ } -+ -+ /** -+ * This method is to be called when a new task has become available to be polled. -+ * The task must already have been added to the data structure that a thread would poll from, -+ * in a way that is visible to any thread (for example by adding it to a concurrent data structure). -+ * Otherwise, the resulting attempt at activating threads would not be able to observe these new tasks yet. -+ *
    -+ * When a task is added that is not important enough to warrant doing a full {@link #update}, -+ * calling this method may be skipped. -+ *
    -+ * Additionally, this method may be called when no new task has been added, but there is a suspicion of new tasks -+ * existing for which no {@link #update} was called. A concrete example of this is when a thread is activated -+ * due to tasks it can poll being available, but then upon activation, acquiring a {@link YieldingLock} it was -+ * waiting for instead. -+ */ -+ public static void newTaskWasAdded(BaseTaskQueueTier tier, TaskSpan span, boolean canBeYieldedTo, boolean onlyIfLastTimeIsTooLongAgo) { -+ -+ if (thereMayBeTasks[tier.ordinal][span.ordinal][canBeYieldedTo ? 1 : 0].getAndIncrement() == 0) { -+ // Always call update() if we just set the thereMayBeTasks value to true -+ onlyIfLastTimeIsTooLongAgo = false; -+ } -+ -+ // Check and update nextAllowedFrequentSignalNewTasksTime -+ if (!onlyIfLastTimeIsTooLongAgo || System.nanoTime() - nextAllowedFrequentSignalNewTasksTime.get() >= 0) { -+ nextAllowedFrequentSignalNewTasksTime.updateAndGet(BaseThreadActivation::updateNextAllowedFrequentSignalNewTasksTime); -+ // Update -+ callForUpdate(); -+ } else { -+ // Do not start an update, but do increment the received calls -+ newUpdateCallsReceived.incrementAndGet(); -+ } -+ -+ } -+ -+ /** -+ * This method is to be called when a {@link YieldingLock} has been released. -+ * The lock must already have been unlocked. Otherwise, the resulting attempt at activating -+ * threads would not be able to observe the lock being released yet. -+ */ -+ public static void yieldingLockWithWaitingThreadsWasUnlocked() { -+ // Update -+ callForUpdate(); -+ } -+ -+ /** -+ * Either starts an {@link #update()}, or lets another thread that is already doing an update know -+ * that it will have to do another one. -+ *
    -+ * Only one thread can be performing an update at a time. -+ * If a second thread calls this method while an update is ongoing -+ * (signified by {@link #updateOngoingOnThread} being non-null), -+ * the thread performing an update will perform another update after finishing the current one, due to the -+ * second thread incrementing {@link #newUpdateCallsReceived}. -+ *
    -+ * After a thread property (or another property that is used in a similar way) -+ * that is used within {@link #update()} is changed, this method must be called. -+ * This currently equates to the following values: -+ *
      -+ *
    • {@link BaseThread#highestTierOfTaskOnStack}
    • -+ *
    • -+ * {@link BaseThread#isWaiting} and {@link BaseThread#lockWaitingFor}, -+ * which are always updated in tandem, and {@link BaseThread#isNotActuallyWaitingYet} and -+ * {@link BaseThread#skipNextWait}, which are set at similar times as {@link BaseThread#isWaiting}. -+ *
    • -+ *
    • -+ * {@link BaseThread#canStartYieldingTasks} and the values -+ * {@link BaseThread#yieldDepth} and {@link BaseThread#heldYieldingLockCount} it depends on. -+ * //TODO Gale We currently do not call callForUpdate just due to changes in heldYieldingLockCount, do we really have to? That would cause a lot of calls. -+ *
    • -+ *
    -+ * This specifically does not include: -+ *
      -+ *
    • -+ * The following values that are only used -+ * in the meta-handling of {@link #update()}, not in the activation of threads: -+ *
        -+ *
      • {@link #newUpdateCallsReceived}
      • -+ *
      • {@link #updateOngoingOnThread}
      • -+ *
      -+ *
    • -+ *
    • -+ * The following values that are never changed outside of {@link #update()}: -+ *
        -+ *
      • {@link #numberOfThreadsActiveForTier}
      • -+ *
      • {@link #threadsWaitingForUnlockedLockForTier}
      • -+ *
      • {@link #numberOfThreadsActiveForLowerThanTier}
      • -+ *
      • {@link #numberOfThreadsActiveForHigherThanTier}
      • -+ *
      • {@link #numberOfThreadsIntendedToBeActiveForTier}
      • -+ *
      -+ *
    • -+ *
    • -+ * {@link #thereMayBeTasks}, which is only set to 0 outside of {@link #update()} -+ * (specifically, in {@link BaseThread}), which will only prevent the {@link #update()} call from -+ * exploring the existence of tasks for a specific {@link BaseTaskQueueTier} and {@link TaskSpan} when -+ * there are in fact no such tasks, thereby not causing any reason to do another update. -+ *
    • -+ *
    -+ */ -+ public static void callForUpdate() { -+ Thread currentThread = Thread.currentThread(); -+ // Make sure the updating thread repeats (must be set before evaluating isUpdateOngoing) -+ newUpdateCallsReceived.incrementAndGet(); -+ // Start the update ourselves if necessary -+ boolean amIServerThread = currentThread instanceof ServerThread; -+ boolean amIDoingUpdate = false; -+ // Start the update if not ongoing -+ if (updateOngoingOnThread.get() == null && updateOngoingOnThread.compareAndSet(null, currentThread)) { -+ amIDoingUpdate = true; -+ } else if (!amIServerThread) { -+ // Take over the update from the server thread if necessary -+ Thread updatePerformingThread = updateOngoingOnThread.get(); -+ if (updatePerformingThread instanceof ServerThread) { -+ // Make sure we are the only thread ready to taking over from the server thread -+ if (!isNonServerThreadReadyToTakeOverUpdate.get() && !isNonServerThreadReadyToTakeOverUpdate.getAndSet(true)) { -+ // Busy wait until the server thread has stopped updating -+ while (updateOngoingOnThread.get() instanceof ServerThread) { -+ Thread.onSpinWait(); -+ } -+ // Start the update, if another thread did not already quickly claim it in the meantime -+ if (updateOngoingOnThread.compareAndSet(null, currentThread)) { -+ amIDoingUpdate = true; -+ } -+ isNonServerThreadReadyToTakeOverUpdate.set(false); -+ } -+ } -+ } -+ -+ if (amIDoingUpdate) { -+ // Perform an update -+ do { -+ try { -+ /* -+ * If newUpdateCallsReceived is zero here, it was set to 0 between the check for whether -+ * it is positive and the setting to true of isUpdateGoing in the while statement below, -+ * or it was set to 0 between the increment and the subsequent setting to true of isUpdateGoing -+ * at the start of this method. -+ */ -+ if (newUpdateCallsReceived.get() > 0) { -+ update(); -+ } -+ } finally { -+ // Take actions to let another thread take over the update -+ boolean isBeingTakenOver = amIServerThread && isNonServerThreadReadyToTakeOverUpdate.get(); -+ if (isBeingTakenOver) { -+ // Make sure an iteration is performed -+ newUpdateCallsReceived.incrementAndGet(); -+ } -+ updateOngoingOnThread.set(null); -+ if (isBeingTakenOver) { -+ // Skip the loop checks -+ break; -+ } -+ } -+ /* -+ If newUpdateCallsReceived is positive here, it was increased between it being set to 0 and -+ updateOngoingOnThread being set to null, so we must repeat. -+ */ -+ } while (newUpdateCallsReceived.get() > 0 && updateOngoingOnThread.get() == null && updateOngoingOnThread.compareAndSet(null, currentThread)); -+ } -+ } -+ -+ /** -+ * Determines whether it could be useful to activate the given thread. -+ * This does not into account whether tasks {@linkplain #thereMayBeTasks may exist} at all, -+ * except for the checking on non-yielding {@link BaseTaskQueueTier#SERVER} tasks for the purpose explained below. -+ *
    -+ * We only activate threads that can start yielding tasks -+ * (it seems wasteful to take the effort to activate threads that can not), -+ * or threads that are waiting for a lock that is not currently locked. -+ *
    -+ * Note that for the server thread, if it cannot start yielding tasks, -+ * there is never an alternative thread that can, so we also allow it, -+ * as a special case, to be activated purely based on the existence of non-yielding tasks. -+ *
    -+ * This method must only be called from {@link #update}. -+ * -+ * @param thread The thread to consider. -+ * @param lockWaitingFor The pre-computed value of {@link BaseThread#lockWaitingFor}. -+ * @param isServerThread Whether the given thread is a {@link ServerThread}. -+ * @return Whether it could be useful to activate the given thread. -+ */ -+ private static boolean couldBeUsefullyActivatedForTasksOrLock(BaseThread thread, @Nullable YieldingLock lockWaitingFor, boolean isServerThread) { -+ if (!thread.isWaitingAndNeedsSignal()) { -+ // There is no point in activating the thread because it is not waiting -+ return false; -+ } -+ if (lockWaitingFor != null && !lockWaitingFor.isLocked() && lockWaitingFor.canBeSignalledFor) { -+ // Activating the thread would be useful because there is a lock that can be acquired -+ return true; -+ } -+ if (thread.canStartYieldingTasks) { -+ // Activating the thread would be useful because it can start yielding tasks -+ return true; -+ } -+ if (isServerThread) { -+ // The server thread can be activated whenever there are any non-yielding tasks -+ for (TaskSpan span : TaskSpan.NON_YIELDING_VALUES) { -+ if (thereMayBeTasks[BaseTaskQueueTier.SERVER.ordinal][span.ordinal][0].get() + thereMayBeTasks[BaseTaskQueueTier.SERVER.ordinal][span.ordinal][1].get() > 0) { -+ return true; -+ } -+ } -+ } -+ /* -+ There is no point in activating this thread (for anything that the thread could do, -+ it would be better to activate a different or newly instantiated thread). -+ */ -+ return false; -+ } -+ -+ /** -+ * Activates threads as necessary, and computes whether threads must de-activate themselves when they can. -+ *
    -+ * This method is called from {@link #callForUpdate()} if necessary. -+ */ -+ static void update() { -+ MinecraftServer.THREAD_DEBUG_LOGGER.ifPresent(it -> it.info("update")); -+ boolean amIServerThread = Thread.currentThread() instanceof ServerThread; -+ boolean madeChangesInLastIteration = false; -+ int numberOfUpdateCallsAtStartOfLastIteration = -1; -+ boolean isFirstIteration = true; -+ /* -+ Keep updating while necessary (while marked to repeat by another call, -+ or while this update itself made some change in the previous iteration, -+ to be sure we only stop when we found no more changes to make). -+ */ -+ updateWhileNecessary: -+ while (true) { -+ MinecraftServer.THREAD_DEBUG_LOGGER.ifPresent(it -> it.info("iteration of update")); -+ -+ // Let a non-server thread take over if needed -+ if (amIServerThread && isNonServerThreadReadyToTakeOverUpdate.get()) { -+ // All preparations for the take-over are performed in #callForUpdate -+ break; -+ } -+ -+ // Break the loop if needed -+ if (isFirstIteration) { -+ // Always run an iteration if this is the first one -+ isFirstIteration = false; -+ numberOfUpdateCallsAtStartOfLastIteration = newUpdateCallsReceived.decrementAndGet(); -+ } else { -+ if (madeChangesInLastIteration) { -+ /* -+ If we made changes in the last iteration, -+ we can quit only if no more update calls have been received at all. -+ */ -+ int oldNewUpdateCallsReceived = newUpdateCallsReceived.getAndUpdate(value -> value == 0 ? 0 : value - 1); -+ if (oldNewUpdateCallsReceived == 0) { -+ break; -+ } -+ numberOfUpdateCallsAtStartOfLastIteration = oldNewUpdateCallsReceived - 1; -+ } else { -+ /* -+ If we made no changes in the last iteration, -+ we can quit if no update calls were received in the meantime. -+ In that case, we can reset newUpdateCallsReceived as we have finished all necessary updates. -+ */ -+ final int finalNumberOfUpdateCallsAtStartOfLastIteration = numberOfUpdateCallsAtStartOfLastIteration; -+ int oldNewUpdateCallsReceived = newUpdateCallsReceived.getAndUpdate(value -> value == finalNumberOfUpdateCallsAtStartOfLastIteration ? 0 : value - 1); -+ if (oldNewUpdateCallsReceived == numberOfUpdateCallsAtStartOfLastIteration) { -+ break; -+ } -+ numberOfUpdateCallsAtStartOfLastIteration = oldNewUpdateCallsReceived - 1; -+ } -+ } -+ -+ // Reset madeChangesInLastIteration -+ madeChangesInLastIteration = false; -+ -+ // Get the threads -+ @Nullable BaseThread @NotNull [] threads = BaseThreadPool.getBaseThreads(); -+ -+ /* -+ Compute for each tier, for how many threads -+ the highest tier of any task on their stack is that tier, -+ and that are not waiting. -+ Additionally, compute the threads for each tier that are waiting for some YieldingLock -+ (threads with no tasks on their stack cannot be waiting for a YieldingLock) -+ that can be unlocked. -+ Additionally, compute the number of threads that are active (i.e. not waiting) -+ but that are not executing a task (i.e. do not have any tasks on their stack). -+ */ -+ Arrays.fill(numberOfThreadsActiveForTier, 0); -+ for (int tierI = 0; tierI < BaseTaskQueueTier.length; tierI++) { -+ threadsWaitingForUnlockedLockForTier[tierI].clear(); -+ } -+ int activeAssistThreadsWithoutTask = 0; -+ for (BaseThread thread : threads) { -+ if (thread != null) { -+ BaseTaskQueueTier tier = thread.highestTierOfTaskOnStack; -+ // This value will be unused if tier is null, so we can safely use some dummy value like 1 -+ int tierOrdinal = thread.baseThreadIndex > 0 ? (tier == null ? -1 : tier.ordinal) : BaseTaskQueueTier.SERVER.ordinal; -+ if (thread.isWaitingAndNeedsSignal()) { -+ var lockWaitingFor = thread.lockWaitingFor; -+ if (lockWaitingFor != null && !lockWaitingFor.isLocked() && lockWaitingFor.canBeSignalledFor) { -+ threadsWaitingForUnlockedLockForTier[tierOrdinal].add(thread); -+ } -+ } else { -+ if (tier == null && thread.baseThreadIndex > 0) { -+ /* -+ ^ Note that assist threads are never waiting for a YieldingLock while they are doing nothing, -+ so we can safely use 'else' below, but the server thread may be waiting for a YieldingLock -+ while it has no highestTierOfTaskOnStack, because it reached a YieldingLock during its -+ normal outer execution. -+ */ -+ /* -+ The thread is doing nothing: -+ if it is also not waiting, it is available to start anything. -+ */ -+ activeAssistThreadsWithoutTask++; -+ } else { -+ numberOfThreadsActiveForTier[tierOrdinal]++; -+ } -+ } -+ } else { -+ numberOfThreadsActiveForTier[BaseTaskQueueTier.SERVER.ordinal]++; -+ } -+ } -+ -+ /* -+ Compute the exclusive cumulative value of numberOfThreadsActiveForTier from above, -+ as being for how many threads the highest tier of any task on their stack -+ is a strictly lower priority tier. -+ */ -+ System.arraycopy(numberOfThreadsActiveForTier, 1, numberOfThreadsActiveForLowerThanTier, 0, BaseTaskQueueTier.length - 1); -+ for (int tierI = BaseTaskQueueTier.length - 2; tierI >= 0; tierI--) { -+ numberOfThreadsActiveForLowerThanTier[tierI] += numberOfThreadsActiveForLowerThanTier[tierI + 1]; -+ } -+ -+ /* -+ Compute the exclusive cumulative value of numberOfThreadsActiveForTier from below, -+ as being for how many threads the highest tier of any task on their stack -+ is a strictly higher priority tier. -+ */ -+ System.arraycopy(numberOfThreadsActiveForTier, 0, numberOfThreadsActiveForHigherThanTier, 1, BaseTaskQueueTier.length - 1); -+ for (int tierI = 2; tierI < BaseTaskQueueTier.length; tierI++) { -+ numberOfThreadsActiveForHigherThanTier[tierI] += numberOfThreadsActiveForHigherThanTier[tierI - 1]; -+ } -+ -+ /* -+ For each tier, compute the number of threads that should be active if there were tasks. -+ This can then later be compared to the actual number of active threads for that tier. -+ */ -+ for (int tierI = 1; tierI < BaseTaskQueueTier.length; tierI++) { -+ numberOfThreadsIntendedToBeActiveForTier[tierI] = BaseThreadPool.targetParallelism - activeAssistThreadsWithoutTask - numberOfThreadsActiveForHigherThanTier[tierI] - Math.min(numberOfThreadsActiveForLowerThanTier[tierI], BaseThreadPool.maxUndisturbedLowerTierThreadCount); -+ } -+ /* -+ There must always be an attempt to have one active server thread. -+ The above computation would sometimes give 0 due to not wanting to disturb lower tiers, -+ which we don't care about for the server thread because it is always more important. -+ The above computation would also sometimes give a number higher than 1 which makes no sense. -+ */ -+ numberOfThreadsIntendedToBeActiveForTier[0] = 1; -+ -+ { -+ final int finalActiveAssistThreadsWithoutTask = activeAssistThreadsWithoutTask; -+ MinecraftServer.THREAD_DEBUG_LOGGER.ifPresent(it -> it.info("Target parallelism = " + BaseThreadPool.targetParallelism + ", active threads without task = " + finalActiveAssistThreadsWithoutTask + ", active threads for tiers = " + Arrays.toString(numberOfThreadsActiveForTier) + ", number of threads intended to be active for tiers = " + Arrays.toString(numberOfThreadsIntendedToBeActiveForTier))); -+ } -+ -+ /* -+ * Determine the highest tier for which the number of threads that are active exceeds -+ * the number of threads that should be active if there were tasks. -+ * If none, set tierInExcessOrdinal to BaseTaskQueueTier#length. -+ */ -+ for (int tierI = 0;; tierI++) { -+ if (tierI == BaseTaskQueueTier.length || numberOfThreadsActiveForTier[tierI] > numberOfThreadsIntendedToBeActiveForTier[tierI]) { -+ tierInExcessOrdinal = tierI; -+ break; -+ } -+ } -+ -+ /* -+ Try to activate a thread, for higher to lower priority tier tasks, in order: -+ if a thread is activated, we continue with another update iteration, so that we make a -+ good-as-possible attempt to activate threads for higher priority tier tasks first. -+ */ -+ for (int tierI = 0; tierI < BaseTaskQueueTier.length; tierI++) { -+ // Only if we need to activate threads -+ if (numberOfThreadsActiveForTier[tierI] < numberOfThreadsIntendedToBeActiveForTier[tierI]) { -+ /* -+ Only if there may be tasks at all (which, if true, will be the reason provided when signalling a -+ thread), or if there is a thread already at this exact tier that is waiting for a YieldingLock. -+ */ -+ boolean thereAreTasks = false; -+ boolean thereAreOnlyTasksThatCanNotBeYieldedTo = false; -+ for (int spanI = 0; spanI < TaskSpan.length; spanI++) { -+ if (thereMayBeTasks[tierI][spanI][1].get() > 0) { -+ thereAreTasks = true; -+ break; -+ } else if (thereMayBeTasks[tierI][spanI][0].get() > 0) { -+ thereAreTasks = true; -+ thereAreOnlyTasksThatCanNotBeYieldedTo = true; -+ break; -+ } -+ } -+ if (thereAreTasks || !threadsWaitingForUnlockedLockForTier[tierI].isEmpty()) { -+ -+ /* -+ * We attempt to wake up a thread that is sleeping, -+ * or add a new thread to start running. -+ * Of course, we can only choose a thread that could poll a task. -+ * We only choose a thread that can accept yielding tasks, even if -+ * the added task is yield-free, so that we have a lower chance of -+ * the chosen thread getting stuck again quickly. -+ * Out of the possible threads, we attempt to choose one that is waiting for a YieldingLock -+ * that is available, so that we have a thread owning this lock as quickly as possible again, -+ * making the next time it is released again sooner as well. -+ * Out of the possible threads that are not waiting for a lock, -+ * we attempt to choose one with non-zero yield depth over any with zero yield depth, -+ * since we must later wake up this thread anyway. Then, we attempt to choose one with the -+ * lowest possible yield depth, so that it can still keep yielding as much as possible. -+ */ -+ /* -+ Special case: only the server thread can start SERVER tasks, -+ and we never activate it for other tiers, because it could only start tiny tasks. -+ */ -+ int tryThreadsStart, tryThreadsEnd; -+ if (tierI == 0) { -+ tryThreadsStart = 0; -+ tryThreadsEnd = 1; -+ } else { -+ tryThreadsStart = 1; -+ tryThreadsEnd = threads.length; -+ } -+ while (true) { -+ // Find the best thread to activate -+ int threadIToUpdate = -1; -+ boolean threadIToUpdateIsWaitingForAvailableYieldingLock = false; -+ int threadIToUpdateYieldDepth = 0; -+ int threadIToUpdateYieldPotential = 0; -+ int threadIToUpdateTierOrdinalOrLength = 0; -+ for (int threadI = tryThreadsStart; threadI < tryThreadsEnd; threadI++) { -+ BaseThread thread = threads[threadI]; -+ if (thread != null) { -+ @Nullable YieldingLock lockWaitingFor = thread.lockWaitingFor; -+ if (couldBeUsefullyActivatedForTasksOrLock(thread, lockWaitingFor, tierI == 0)) { -+ /* -+ Tasks of a certain tier may yield to tasks of the same or a higher -+ tier, and they may also yield to tiny tasks of a lower tier. -+ We do not want to wake up a thread just for tiny tasks -+ unless it has zero yield depth, -+ so we only activate threads that have either no tasks on their stack, -+ or only tasks of the same or a lower tier, where a lower priority tier -+ is preferred (but not as important as the yield depth). -+ Of course, this only takes into account tasks, and we may also -+ activate threads due to them waiting on an available YieldingLock. -+ */ -+ var highestTierOfTaskOnStack = thread.highestTierOfTaskOnStack; -+ var highestTierOfTaskOnStackOrdinalOrLength = highestTierOfTaskOnStack == null ? BaseTaskQueueTier.length : highestTierOfTaskOnStack.ordinal; -+ boolean isThreadWaitingForAvailableYieldingLock = lockWaitingFor != null && !lockWaitingFor.isLocked() && lockWaitingFor.canBeSignalledFor; -+ if (isThreadWaitingForAvailableYieldingLock || highestTierOfTaskOnStack == null || highestTierOfTaskOnStack.ordinal >= tierI) { -+ int yieldDepth = thread.yieldDepth; -+ if (!thereAreOnlyTasksThatCanNotBeYieldedTo || yieldDepth == 0) { -+ boolean isBestChoice = false; -+ int yieldPotential = thread.maximumYieldDepth - yieldDepth; -+ if (threadIToUpdate == -1) { -+ isBestChoice = true; -+ } else if (isThreadWaitingForAvailableYieldingLock != threadIToUpdateIsWaitingForAvailableYieldingLock) { -+ isBestChoice = isThreadWaitingForAvailableYieldingLock; -+ } else if (threadIToUpdateYieldDepth == 0 && yieldDepth != 0) { -+ isBestChoice = true; -+ } else if (yieldDepth != 0) { -+ if (yieldPotential > threadIToUpdateYieldPotential) { -+ isBestChoice = true; -+ } else if (highestTierOfTaskOnStackOrdinalOrLength > threadIToUpdateTierOrdinalOrLength) { -+ isBestChoice = true; -+ } -+ } -+ if (isBestChoice) { -+ threadIToUpdate = threadI; -+ threadIToUpdateIsWaitingForAvailableYieldingLock = isThreadWaitingForAvailableYieldingLock; -+ threadIToUpdateYieldDepth = yieldDepth; -+ threadIToUpdateYieldPotential = yieldPotential; -+ threadIToUpdateTierOrdinalOrLength = highestTierOfTaskOnStackOrdinalOrLength; -+ } -+ } -+ } -+ } -+ } -+ } -+ if (threadIToUpdate == -1) { -+ // No valid thread was found -+ break; -+ } -+ // Check if the thread still seems valid and attempt to activate it -+ BaseThread thread = threads[threadIToUpdate]; -+ @Nullable YieldingLock lockWaitingFor = thread.lockWaitingFor; -+ if (couldBeUsefullyActivatedForTasksOrLock(thread, lockWaitingFor, tierI == 0)) { -+ // Wake up the thread -+ if (thread.signal(thereAreTasks ? SignalReason.TASK : SignalReason.YIELDING_LOCK)) { -+ // Make sure no other threads are activated for the same unlocking event -+ // Do another update -+ madeChangesInLastIteration = true; -+ continue updateWhileNecessary; -+ } -+ } -+ /* -+ The thread was not valid to activate anymore, or not activated, -+ so we attempt to find a valid thread again. -+ */ -+ } -+ -+ // Because no thread was activated, we add one (only if we were looking for an AssistThread) -+ if (tierI != 0) { -+ BaseThreadPool.addAssistThread(); -+ // Do another update -+ madeChangesInLastIteration = true; -+ continue updateWhileNecessary; -+ } -+ -+ } -+ } -+ } -+ -+ } -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadPool.java b/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadPool.java -new file mode 100644 -index 0000000000000000000000000000000000000000..07285dac8bc99f0af7702f61765e3311a317f708 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadPool.java -@@ -0,0 +1,218 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.thread.pool; -+ -+import net.minecraft.server.MinecraftServer; -+import org.galemc.gale.executor.queue.BaseTaskQueueTier; -+import org.galemc.gale.executor.thread.AssistThread; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.ServerThread; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.Arrays; -+ -+/** -+ * A pool of threads that can perform tasks to assist the current {@link ServerThread}. These tasks can be of -+ * different {@linkplain BaseTaskQueueTier tiers}. -+ *
    -+ * This pool intends to keep {@link #targetParallelism} threads active at any time, -+ * which includes a potentially active {@link ServerThread}. -+ *
    -+ * As such, this pool is closely intertwined with the {@link ServerThread}. This pool can not control the -+ * {@link ServerThread} in any way, but it is responsible for signalling the {@link ServerThread} when tasks become -+ * available in a {@link BaseTaskQueueTier#SERVER} task queue, and for listening for when the {@link ServerThread} -+ * becomes (in)active in order to update the number of active {@link AssistThread}s accordingly. -+ *

    -+ * Updates to the threads in this pool are done in a lock-free manner that attempts to do the right thing with -+ * the volatile information that is available. In some cases, this may cause a thread to be woken up when it -+ * should not have been, and so on, but the updates being lock-free is more significant than the updates being -+ * optimal in a high-contention environment. The environment is not expected to have high enough contention for -+ * this to have much of an impact. Additionally, the suboptimalities in updates are always optimistic in terms of -+ * making/keeping threads active rather than inactive, and can not a situation where a thread was intended -+ * to be active, but ends but not being active. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public final class BaseThreadPool { -+ -+ private BaseThreadPool() {} -+ -+ public static final String targetParallelismEnvironmentVariable = "gale.threads.target"; -+ public static final String maxUndisturbedLowerTierThreadCountEnvironmentVariable = "gale.threads.undisturbed"; -+ -+ /** -+ * The target number of threads that will be actively in use by this pool, -+ * which includes a potentially active {@link ServerThread}. -+ *
    -+ * This value is always positive. -+ *
    -+ * The value is currently automatically determined according to the following table: -+ * -+ * -+ * -+ * -+ * -+ * -+ * -+ * -+ * -+ * -+ * -+ * -+ * -+ * -+ * -+ *
    system threadsthreads spared
    ≤ 30
    [4, 14]1
    [15, 23]2
    [24, 37]3
    [38, 54]4
    [55, 74]5
    [75, 99]6
    [100, 127]7
    [128, 158]8
    [159, 193]9
    [194, 232]10
    [233, 274]11
    ≥ 27512
    -+ * Then target parallelism = system threads - threads spared. -+ *
    -+ * The computed value above can be overridden using the {@link #targetParallelismEnvironmentVariable}. -+ */ -+ public static final int targetParallelism; -+ static { -+ int parallelismByEnvironmentVariable = Integer.getInteger(targetParallelismEnvironmentVariable, -1); -+ int targetParallelismBeforeSetAtLeastOne; -+ if (parallelismByEnvironmentVariable >= 0) { -+ targetParallelismBeforeSetAtLeastOne = parallelismByEnvironmentVariable; -+ } else { -+ int systemThreads = Runtime.getRuntime().availableProcessors(); -+ int threadsSpared; -+ if (systemThreads <= 3) { -+ threadsSpared = 0; -+ } else if (systemThreads <= 14) { -+ threadsSpared = 1; -+ } else if (systemThreads <= 23) { -+ threadsSpared = 2; -+ } else if (systemThreads <= 37) { -+ threadsSpared = 3; -+ } else if (systemThreads <= 54) { -+ threadsSpared = 4; -+ } else if (systemThreads <= 74) { -+ threadsSpared = 5; -+ } else if (systemThreads <= 99) { -+ threadsSpared = 6; -+ } else if (systemThreads <= 127) { -+ threadsSpared = 7; -+ } else if (systemThreads <= 158) { -+ threadsSpared = 8; -+ } else if (systemThreads <= 193) { -+ threadsSpared = 9; -+ } else if (systemThreads <= 232) { -+ threadsSpared = 10; -+ } else if (systemThreads <= 274) { -+ threadsSpared = 11; -+ } else { -+ threadsSpared = 12; -+ } -+ targetParallelismBeforeSetAtLeastOne = systemThreads - threadsSpared; -+ } -+ targetParallelism = Math.max(1, targetParallelismBeforeSetAtLeastOne); -+ } -+ -+ /** -+ * The maximum number of threads to be executing tasks, that only have tasks on their thread that are strictly -+ * below a certain tier, before a thread wishing to execute such tasks gets activated regardless. -+ * If this threshold of lower tier threads is not exceeded, activating a thread to execute a higher tier task -+ * will be delayed until one of the active threads finishes execution of their stack or blocks for another -+ * reason. -+ *
    -+ * This value is always nonnegative. -+ *
    -+ * This value is currently automatically determined according to the following rule: -+ *
      -+ *
    • 0, if {@link #targetParallelism} = 1
    • -+ *
    • {@code max(1, floor(2/5 * }{@link #targetParallelism}{@code ))}
    • -+ *
    -+ * The computed value above can be overridden using the {@link #maxUndisturbedLowerTierThreadCountEnvironmentVariable}. -+ */ -+ public static final int maxUndisturbedLowerTierThreadCount; -+ static { -+ int maxUndisturbedLowerTierThreadCountByEnvironmentVariable = Integer.getInteger(maxUndisturbedLowerTierThreadCountEnvironmentVariable, -1); -+ maxUndisturbedLowerTierThreadCount = maxUndisturbedLowerTierThreadCountByEnvironmentVariable >= 0 ? maxUndisturbedLowerTierThreadCountByEnvironmentVariable : targetParallelism == 1 ? 0 : Math.max(1, targetParallelism * 2 / 5); -+ } -+ -+ /** -+ * An array of the {@link AssistThread}s in this pool, indexed by their {@link AssistThread#assistThreadIndex}. -+ *
    -+ * This field must only ever be changed from within {@link #addAssistThread}. -+ */ -+ private static volatile AssistThread[] assistThreads = new AssistThread[0]; -+ -+ /** -+ * An array of the {@link BaseThread}s in this pool, indexed by their {@link BaseThread#baseThreadIndex}. -+ *
    -+ * This field must not be referenced anywhere outside {@link #addAssistThread} or {@link #getBaseThreads()}: -+ * it only holds the last computed value. -+ */ -+ private static volatile @Nullable BaseThread @NotNull [] lastComputedBaseThreads = new BaseThread[1]; -+ -+ /** -+ * Creates a new {@link AssistThread}, adds it to this pool and starts it. -+ *
    -+ * Must only be called from within {@link BaseThreadActivation#update()} while -+ * {@link BaseThreadActivation#updateOngoingOnThread} is not null. -+ */ -+ public static void addAssistThread() { -+ int oldThreadsLength = assistThreads.length; -+ int newThreadsLength = oldThreadsLength + 1; -+ // Expand the thread array -+ AssistThread[] newAssistThreads = Arrays.copyOf(assistThreads, newThreadsLength); -+ // Create the new thread -+ AssistThread newThread = newAssistThreads[oldThreadsLength] = new AssistThread(oldThreadsLength); -+ // Save the new thread array -+ assistThreads = newAssistThreads; -+ // Update the assist threads in baseThreads -+ @SuppressWarnings("NonAtomicOperationOnVolatileField") -+ BaseThread[] newLastComputedBaseThreads = lastComputedBaseThreads = Arrays.copyOf(lastComputedBaseThreads, newThreadsLength + 1); -+ newLastComputedBaseThreads[newThreadsLength] = newThread; -+ // Start the thread -+ newThread.start(); -+ MinecraftServer.THREAD_DEBUG_LOGGER.ifPresent(it -> it.info("Added assist thread " + newAssistThreads.length)); -+ } -+ -+ /** -+ * The {@link BaseThread}s ({@link ServerThread}s and {@link AssistThread}s) in this thread pool, -+ * specifically for the purpose of easy iteration. -+ *
    -+ * Note that the {@link ServerThread} at index 0 may be null if {@link MinecraftServer#isConstructed} is false. -+ *
    -+ * Must only be called from within {@link BaseThreadActivation#update()} while -+ * {@link BaseThreadActivation#updateOngoingOnThread} is not null. -+ */ -+ static @Nullable BaseThread @NotNull [] getBaseThreads() { -+ // Store in a non-local volatile -+ @Nullable BaseThread @NotNull [] baseThreads = lastComputedBaseThreads; -+ // Update the server thread if necessary -+ baseThreads[0] = ServerThread.getInstanceIfConstructed(); -+ // Return the value -+ return baseThreads; -+ } -+ -+ /** -+ * This method must not be called with {@code index} = 0 while {@link MinecraftServer#isConstructed} is false. -+ * -+ * @return The {@link BaseThread} with the given {@link BaseThread#baseThreadIndex}. -+ * This must not be called -+ */ -+ public static @NotNull BaseThread getThreadByBaseIndex(int index) { -+ if (index == 0) { -+ return ServerThread.getInstance(); -+ } -+ return assistThreads[index - 1]; -+ } -+ -+ /** -+ * @return The same value as {@link #getThreadByBaseIndex} if {@link MinecraftServer#isConstructed} is true -+ * or if the given {@code index} is not 0, -+ * or null otherwise (i.e. if {@link MinecraftServer#isConstructed} is false and the given {@code index} is 0). -+ */ -+ @SuppressWarnings("unused") -+ public static @Nullable BaseThread getThreadByBaseIndexIfConstructed(int index) { -+ return index != 0 || MinecraftServer.isConstructed ? getThreadByBaseIndex(index) : null; -+ } -+ -+ public static AssistThread getThreadByAssistIndex(int index) { -+ return assistThreads[index]; -+ } -+ -+} -diff --git a/src/main/java/org/spigotmc/SpigotCommand.java b/src/main/java/org/spigotmc/SpigotCommand.java -index 3112a8695639c402e9d18710acbc11cff5611e9c..7b38565b8699bd083c2114981feb2202321b8486 100644 ---- a/src/main/java/org/spigotmc/SpigotCommand.java -+++ b/src/main/java/org/spigotmc/SpigotCommand.java -@@ -31,7 +31,7 @@ public class SpigotCommand extends Command { - - MinecraftServer console = MinecraftServer.getServer(); - org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); -- for (ServerLevel world : console.getAllLevels()) { -+ for (ServerLevel world : console.getAllLevelsArray()) { // Gale - base thread pool - optimize server levels - world.spigotConfig.init(); - } - console.server.reloadCount++; -diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java -index f29e48fc653affca2338d842a1a4972c5fa67f01..bcb144ec4a836b8b32f60726bcbee218a4f62742 100644 ---- a/src/main/java/org/spigotmc/WatchdogThread.java -+++ b/src/main/java/org/spigotmc/WatchdogThread.java -@@ -7,12 +7,13 @@ import java.util.logging.Level; - import java.util.logging.Logger; - import net.minecraft.server.MinecraftServer; - import org.bukkit.Bukkit; -+import org.galemc.gale.executor.thread.ServerThread; - --public final class WatchdogThread extends io.papermc.paper.util.TickThread // Paper - rewrite chunk system -+public final class WatchdogThread extends ServerThread // Paper - rewrite chunk system // Gale - base thread pool - { - - public static final boolean DISABLE_WATCHDOG = Boolean.getBoolean("disable.watchdog"); // Paper -- private static WatchdogThread instance; -+ public static WatchdogThread instance; // Gale - base thread pool - private -> public - private long timeoutTime; - private boolean restart; - private final long earlyWarningEvery; // Paper - Timeout time for just printing a dump but not restarting -@@ -205,7 +206,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa - log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Gale!):" ); // Paper // Gale - branding changes - io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(isLongTimeout); // Paper // Paper - rewrite chunk system - this.dumpTickingInfo(); // Paper - log detailed tick information -- WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); -+ WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.serverThread.getId(), Integer.MAX_VALUE ), log ); // Gale - base thread pool - log.log( Level.SEVERE, "------------------------------" ); - // - // Paper start - Only print full dump on long timeouts diff --git a/patches/server/0152-Watch-for-blocking-base-threads.patch b/patches/server/0152-Watch-for-blocking-base-threads.patch deleted file mode 100644 index e42ebba..0000000 --- a/patches/server/0152-Watch-for-blocking-base-threads.patch +++ /dev/null @@ -1,116 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Fri, 3 Feb 2023 23:52:49 +0100 -Subject: [PATCH] Watch for blocking base threads - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/build.gradle.kts b/build.gradle.kts -index 394b5044090c1c7b00b30036e4b8be4a6ac58ed1..0f0895ca1faa1e4bbd96614b497efd6a6ced5e00 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -15,6 +15,7 @@ dependencies { - exclude("io.papermc.paper", "paper-api") - } - // Gale end - project setup -+ implementation("io.projectreactor.tools:blockhound:1.0.7.RELEASE") // Gale - base thread pool - watch for blocking base threads - // Paper start - implementation("org.jline:jline-terminal-jansi:3.21.0") - implementation("net.minecrell:terminalconsoleappender:1.3.0") -@@ -179,6 +180,7 @@ fun TaskContainer.registerRunTask( - if (providers.gradleProperty("paper.runDisableWatchdog").getOrElse("false") == "true") { - systemProperty("disable.watchdog", true) - } -+ //systemProperty("gale.detect.thread.blocks", true) // Gale - base thread pool - watch for blocking base threads - - val memoryGb = providers.gradleProperty("paper.runMemoryGb").getOrElse("2") - minHeapSize = "${memoryGb}G" -@@ -188,6 +190,7 @@ fun TaskContainer.registerRunTask( - jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") - // Gale end - enable virtual threads for development runs - jvmArgs("--add-modules=jdk.incubator.vector") // Gale - Pufferfish - SIMD support -+ jvmArgs("-XX:+AllowRedefinitionToAddDeleteMethods") // Gale - base thread pool - watch for blocking base threads - - doFirst { - workingDir.mkdirs() -diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index 39f8b19064f30059f94baf28f3493f19b5ba29de..74eee94f75391ef2d16a5becbfa498fde96526e7 100644 ---- a/src/main/java/org/bukkit/craftbukkit/Main.java -+++ b/src/main/java/org/bukkit/craftbukkit/Main.java -@@ -4,16 +4,21 @@ import java.io.File; - import java.io.IOException; - import java.text.DateFormat; - import java.text.SimpleDateFormat; --import java.util.Arrays; --import java.util.Calendar; --import java.util.Date; --import java.util.List; -+import java.util.*; - import java.util.concurrent.TimeUnit; -+import java.util.function.Consumer; - import java.util.logging.Level; - import java.util.logging.Logger; - import joptsimple.OptionParser; - import joptsimple.OptionSet; - import joptsimple.util.PathConverter; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.util.ExceptionCollector; -+import net.minecraft.world.level.lighting.LayerLightEventListener; -+import net.minecrell.terminalconsole.TerminalConsoleAppender; // Paper -+import org.galemc.gale.executor.thread.BaseThread; -+import org.spigotmc.WatchdogThread; -+import reactor.blockhound.BlockHound; - - public class Main { - public static boolean useJline = true; -@@ -46,6 +51,49 @@ public class Main { - // Gale end - include time in startup logs - - public static void main(String[] args) { -+ // Gale start - base thread pool - watch for blocking base threads -+ if (Boolean.getBoolean("gale.detect.thread.blocks")) { -+ printlnStartupInfoToSystemOut("Initializing blocking base thread detection..."); -+ try { -+ var builder = BlockHound.builder(); -+ // Mark base threads as intended to be non-blocking (except the WatchdogThread, which obviously sleeps until needed) -+ builder.nonBlockingThreadPredicate(currentPredicate -> currentPredicate.or(thread -> thread instanceof BaseThread && !(thread instanceof WatchdogThread))); -+ // Set the callback when a base thread blocks -+ builder.blockingMethodCallback(blockingMethod -> { -+ if (MinecraftServer.SERVER == null) { -+ // Allow blocking before the server has finished initializing -+ return; -+ } -+ String message = "A base thread (" + Thread.currentThread() + ") started blocking:"; -+ if (MinecraftServer.LOGGER != null) { -+ MinecraftServer.LOGGER.error(message); -+ } else { -+ printlnStartupErrorToSystemOut(message); -+ } -+ new Error(blockingMethod.toString()).printStackTrace(); -+ }); -+ // Allow busy waiting -+ var blockingMethodsField = builder.getClass().getDeclaredField("blockingMethods"); -+ blockingMethodsField.setAccessible(true); -+ Map>> blockingMethods = (Map>>) blockingMethodsField.get(builder); -+ Map> threadBlockingMethods = blockingMethods.get("java/lang/Thread"); -+ threadBlockingMethods.remove("onSpinWait"); -+ threadBlockingMethods.remove("yield"); -+ // Allow base threads to block in the intended way -+ builder.allowBlockingCallsInside("org.galemc.gale.executor.thread.BaseThread", "waitUntilSignalled"); -+ // Allow the ServerThread to block during initialization -+ builder.allowBlockingCallsInside("net.minecraft.server.dedicated.DedicatedServer", "initServer"); -+ // Install BlockHound -+ builder.install(); -+ printlnStartupInfoToSystemOut("Blocking base thread detection is enabled."); -+ } catch (Exception e) { -+ // BlockHound instrumentation failed, which probably means the needed JVM flag is missing -+ printlnStartupInfoToSystemOut("Blocking base thread detection is disabled."); -+ printlnStartupInfoToSystemOut("When it is enabled, unexpected thread blocks can be automatically resolved to improve performance."); -+ printlnStartupInfoToSystemOut("To enable it, add \"-XX:+AllowRedefinitionToAddDeleteMethods\" to your startup flags, BEFORE the \"-jar\"."); -+ } -+ } -+ // Gale end - base thread pool - watch for blocking base threads - // Paper start - final String warnWhenLegacyFormattingDetected = String.join(".", "net", "kyori", "adventure", "text", "warnWhenLegacyFormattingDetected"); - if (false && System.getProperty(warnWhenLegacyFormattingDetected) == null) { diff --git a/patches/server/0153-Non-blocking-PooledObjects.patch b/patches/server/0153-Non-blocking-PooledObjects.patch deleted file mode 100644 index 1ef0a96..0000000 --- a/patches/server/0153-Non-blocking-PooledObjects.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Thu, 22 Dec 2022 16:14:50 +0100 -Subject: [PATCH] Non-blocking PooledObjects - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/com/destroystokyo/paper/util/pooled/PooledObjects.java b/src/main/java/com/destroystokyo/paper/util/pooled/PooledObjects.java -index a743703502cea333bd4231b6557de50e8eaf81eb..5531c681b3547c32f4fc8ec86bb722e1ee653826 100644 ---- a/src/main/java/com/destroystokyo/paper/util/pooled/PooledObjects.java -+++ b/src/main/java/com/destroystokyo/paper/util/pooled/PooledObjects.java -@@ -2,11 +2,18 @@ package com.destroystokyo.paper.util.pooled; - - import io.papermc.paper.util.MCUtil; - import org.apache.commons.lang3.mutable.MutableInt; -+import org.galemc.gale.concurrent.Mutex; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; - - import java.util.ArrayDeque; - import java.util.function.Consumer; - import java.util.function.Supplier; - -+// Gale start - non-blocking PooledObjects -+@AnyThreadSafe -+@YieldFree -+// Gale end - non-blocking PooledObjects - public final class PooledObjects { - - /** -@@ -36,6 +43,7 @@ public final class PooledObjects { - private final Consumer releaser; - private final int maxPoolSize; - private final ArrayDeque queue; -+ private final Mutex queueLock = Mutex.create(); // Gale - non-blocking PooledObjects - - public PooledObjects(final Supplier creator, int maxPoolSize) { - this(creator, maxPoolSize, null); -@@ -66,7 +74,7 @@ public final class PooledObjects { - - public final E acquire() { - E value; -- synchronized (queue) { -+ try (var ignored = this.queueLock.withSpinLock()) { // Gale - non-blocking PooledObjects - value = this.queue.pollLast(); - } - return value != null ? value : this.creator.get(); -@@ -76,7 +84,7 @@ public final class PooledObjects { - if (this.releaser != null) { - this.releaser.accept(value); - } -- synchronized (this.queue) { -+ try (var ignored = this.queueLock.withSpinLock()) { // Gale - non-blocking PooledObjects - if (queue.size() < this.maxPoolSize) { - this.queue.addLast(value); - } diff --git a/patches/server/0154-Yielding-memoized-Supplier.patch b/patches/server/0154-Yielding-memoized-Supplier.patch deleted file mode 100644 index 5012615..0000000 --- a/patches/server/0154-Yielding-memoized-Supplier.patch +++ /dev/null @@ -1,388 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Fri, 3 Feb 2023 23:17:33 +0100 -Subject: [PATCH] Yielding memoized Supplier - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java -index 760601de028536aec142d195aa875ae59edd8e41..4753d4b489724d5b2681ac1c9f3736f08bfafbd2 100644 ---- a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java -+++ b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java -@@ -41,6 +41,7 @@ import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; - import org.apache.commons.lang3.RandomStringUtils; - import org.bukkit.configuration.ConfigurationSection; - import org.bukkit.configuration.file.YamlConfiguration; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - import org.jetbrains.annotations.VisibleForTesting; - import org.slf4j.Logger; - import org.spigotmc.SpigotConfig; -@@ -127,7 +128,7 @@ public class PaperConfigurations extends Configurations SPIGOT_WORLD_DEFAULTS = Suppliers.memoize(() -> new SpigotWorldConfig(RandomStringUtils.randomAlphabetic(255)) { // Gale - Gale configuration -+ public static final Supplier SPIGOT_WORLD_DEFAULTS = new YieldingMemoizedSupplier<>(() -> new SpigotWorldConfig(RandomStringUtils.randomAlphabetic(255)) { // Gale - Gale configuration, yielding memoized Supplier - @Override // override to ensure "verbose" is false - public void init() { - SpigotConfig.readConfig(SpigotWorldConfig.class, this); -diff --git a/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java b/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java -index 0627c98cae0b5ebdd71a849ae1299d7d3d581850..b0ea72d8a186a8e2758d0b2e3204cc81463085d4 100644 ---- a/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java -+++ b/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java -@@ -15,6 +15,7 @@ import net.minecraft.commands.CommandSourceStack; - import net.minecraft.network.chat.ComponentUtils; - import net.minecraft.server.dedicated.DedicatedServer; - import org.checkerframework.checker.nullness.qual.NonNull; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - import org.jline.reader.Candidate; - import org.jline.reader.LineReader; - import org.jline.reader.ParsedLine; -@@ -27,7 +28,7 @@ public final class BrigadierCommandCompleter { - - public BrigadierCommandCompleter(final @NonNull DedicatedServer server) { - this.server = server; -- this.commandSourceStack = Suppliers.memoize(this.server::createCommandSourceStack); -+ this.commandSourceStack = new YieldingMemoizedSupplier<>(this.server::createCommandSourceStack); // Gale - yielding memoized Supplier - } - - public void complete(final @NonNull LineReader reader, final @NonNull ParsedLine line, final @NonNull List candidates, final @NonNull List existing) { -diff --git a/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java b/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java -index dd9d77d7c7f1a5a130a1f4c15e5b1e68ae3753e1..c3661b25ae8b4dc5e9d7e9bb0e14f6d2b314f309 100644 ---- a/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java -+++ b/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java -@@ -9,6 +9,7 @@ import java.util.regex.Pattern; - import net.minecraft.commands.CommandSourceStack; - import net.minecraft.server.dedicated.DedicatedServer; - import org.checkerframework.checker.nullness.qual.NonNull; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - import org.jline.reader.Highlighter; - import org.jline.reader.LineReader; - import org.jline.utils.AttributedString; -@@ -22,7 +23,7 @@ public final class BrigadierCommandHighlighter implements Highlighter { - - public BrigadierCommandHighlighter(final @NonNull DedicatedServer server) { - this.server = server; -- this.commandSourceStack = Suppliers.memoize(this.server::createCommandSourceStack); -+ this.commandSourceStack = new YieldingMemoizedSupplier<>(this.server::createCommandSourceStack); // Gale - yielding memoized Supplier - } - - @Override -diff --git a/src/main/java/net/minecraft/util/ExtraCodecs.java b/src/main/java/net/minecraft/util/ExtraCodecs.java -index 57fae84be749bd4a61a0c700a5cefce7122643e6..c695b59c17f441ab7e2065817c0c5242866c0e55 100644 ---- a/src/main/java/net/minecraft/util/ExtraCodecs.java -+++ b/src/main/java/net/minecraft/util/ExtraCodecs.java -@@ -1,7 +1,6 @@ - package net.minecraft.util; - - import com.google.common.annotations.VisibleForTesting; --import com.google.common.base.Suppliers; - import com.google.common.collect.ImmutableList; - import com.google.gson.JsonElement; - import com.google.gson.JsonParseException; -@@ -52,6 +51,7 @@ import org.apache.commons.lang3.mutable.MutableObject; - import org.joml.AxisAngle4f; - import org.joml.Matrix4f; - import org.joml.Quaternionf; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - import org.joml.Vector3f; - - public class ExtraCodecs { -@@ -542,7 +542,7 @@ public class ExtraCodecs { - - static record LazyInitializedCodec
    (Supplier> delegate) implements Codec { - LazyInitializedCodec { -- delegate = Suppliers.memoize(delegate::get); // Gale - dev import deobfuscation fixes -+ delegate = new YieldingMemoizedSupplier<>(delegate::get); // Gale - dev import deobfuscation fixes, yielding memoized Supplier - } - - public DataResult> decode(DynamicOps dynamicOps, T object) { -diff --git a/src/main/java/net/minecraft/util/LazyLoadedValue.java b/src/main/java/net/minecraft/util/LazyLoadedValue.java -index 906d4bca809fe2ae6b45e7212be8547b81422b01..3ceff89ef71768826be1156538dc70f25d68488a 100644 ---- a/src/main/java/net/minecraft/util/LazyLoadedValue.java -+++ b/src/main/java/net/minecraft/util/LazyLoadedValue.java -@@ -1,6 +1,7 @@ - package net.minecraft.util; - --import com.google.common.base.Suppliers; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; -+ - import java.util.function.Supplier; - - /** @deprecated */ -@@ -9,7 +10,7 @@ public class LazyLoadedValue { - private final Supplier factory; - - public LazyLoadedValue(Supplier delegate) { -- this.factory = Suppliers.memoize(delegate::get); -+ this.factory = new YieldingMemoizedSupplier<>(delegate); // Gale - yielding memoized Supplier - } - - public T get() { -diff --git a/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java b/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java -index d6de68b4b0edabf37ba551b91bbe6682895b2a82..b4cf65c1b84a4ebcf4a362ec614f5fd7a585ab2e 100644 ---- a/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java -+++ b/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java -@@ -1,6 +1,5 @@ - package net.minecraft.util.datafix.fixes; - --import com.google.common.base.Suppliers; - import com.google.common.collect.ImmutableMap; - import com.google.common.collect.ImmutableSet; - import com.mojang.datafixers.DSL; -@@ -24,6 +23,7 @@ import java.util.function.Supplier; - import java.util.stream.Stream; - import javax.annotation.Nullable; - import org.apache.commons.lang3.mutable.MutableInt; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - - public class ChunkProtoTickListFix extends DataFix { - private static final int SECTION_WIDTH = 16; -@@ -64,7 +64,7 @@ public class ChunkProtoTickListFix extends DataFix { - } - - typedx2.getOptionalTyped(opticFinder4).ifPresent((typed3) -> { // Gale - dev import deobfuscation fixes -- int2ObjectMap.put(i, Suppliers.memoize(() -> { -+ int2ObjectMap.put(i, new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier - // Gale start - dev import deobfuscation fixes - List> list = typed.getOptionalTyped(opticFinder6).map((typedx3) -> { - return typedx3.write().result().map((dynamic3) -> { -diff --git a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java -index c84908095a93d42826b21bf5f3490410fb0a5708..6d2ef064400304fb96f9ec5174b926371d01ed7c 100644 ---- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java -@@ -33,6 +33,7 @@ import net.minecraft.world.flag.FeatureFlagSet; - import net.minecraft.world.item.ItemStack; - import net.minecraft.world.level.block.Block; - import net.minecraft.world.level.block.entity.BlockEntity; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - import org.slf4j.Logger; - - // CraftBukkit start -@@ -238,7 +239,7 @@ public abstract class AbstractContainerMenu { - ItemStack itemstack = ((Slot) this.slots.get(i)).getItem(); - - Objects.requireNonNull(itemstack); -- Supplier supplier = Suppliers.memoize(itemstack::copy); -+ Supplier supplier = new YieldingMemoizedSupplier<>(itemstack::copy); // Gale - yielding memoized Supplier - - this.triggerSlotListeners(i, itemstack, supplier); - this.synchronizeSlotToRemote(i, itemstack, supplier); -diff --git a/src/main/java/net/minecraft/world/item/HoneycombItem.java b/src/main/java/net/minecraft/world/item/HoneycombItem.java -index f0b720eafc538f97d788f89bd2f2e9da0ff84a19..d12020b2eca0130340436d38247f933f6b246f01 100644 ---- a/src/main/java/net/minecraft/world/item/HoneycombItem.java -+++ b/src/main/java/net/minecraft/world/item/HoneycombItem.java -@@ -16,12 +16,13 @@ import net.minecraft.world.level.block.Block; - import net.minecraft.world.level.block.Blocks; - import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.level.gameevent.GameEvent; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - - public class HoneycombItem extends Item { -- public static final Supplier> WAXABLES = Suppliers.memoize(() -> { -+ public static final Supplier> WAXABLES = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier - return ImmutableBiMap.builder().put(Blocks.COPPER_BLOCK, Blocks.WAXED_COPPER_BLOCK).put(Blocks.EXPOSED_COPPER, Blocks.WAXED_EXPOSED_COPPER).put(Blocks.WEATHERED_COPPER, Blocks.WAXED_WEATHERED_COPPER).put(Blocks.OXIDIZED_COPPER, Blocks.WAXED_OXIDIZED_COPPER).put(Blocks.CUT_COPPER, Blocks.WAXED_CUT_COPPER).put(Blocks.EXPOSED_CUT_COPPER, Blocks.WAXED_EXPOSED_CUT_COPPER).put(Blocks.WEATHERED_CUT_COPPER, Blocks.WAXED_WEATHERED_CUT_COPPER).put(Blocks.OXIDIZED_CUT_COPPER, Blocks.WAXED_OXIDIZED_CUT_COPPER).put(Blocks.CUT_COPPER_SLAB, Blocks.WAXED_CUT_COPPER_SLAB).put(Blocks.EXPOSED_CUT_COPPER_SLAB, Blocks.WAXED_EXPOSED_CUT_COPPER_SLAB).put(Blocks.WEATHERED_CUT_COPPER_SLAB, Blocks.WAXED_WEATHERED_CUT_COPPER_SLAB).put(Blocks.OXIDIZED_CUT_COPPER_SLAB, Blocks.WAXED_OXIDIZED_CUT_COPPER_SLAB).put(Blocks.CUT_COPPER_STAIRS, Blocks.WAXED_CUT_COPPER_STAIRS).put(Blocks.EXPOSED_CUT_COPPER_STAIRS, Blocks.WAXED_EXPOSED_CUT_COPPER_STAIRS).put(Blocks.WEATHERED_CUT_COPPER_STAIRS, Blocks.WAXED_WEATHERED_CUT_COPPER_STAIRS).put(Blocks.OXIDIZED_CUT_COPPER_STAIRS, Blocks.WAXED_OXIDIZED_CUT_COPPER_STAIRS).build(); - }); -- public static final Supplier> WAX_OFF_BY_BLOCK = Suppliers.memoize(() -> { -+ public static final Supplier> WAX_OFF_BY_BLOCK = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier - return WAXABLES.get().inverse(); - }); - -diff --git a/src/main/java/net/minecraft/world/level/PathNavigationRegion.java b/src/main/java/net/minecraft/world/level/PathNavigationRegion.java -index efe922810507c96183a56a5e81a7b14214d8747b..b87c9a6189161e776fa488c1ac6215edca596184 100644 ---- a/src/main/java/net/minecraft/world/level/PathNavigationRegion.java -+++ b/src/main/java/net/minecraft/world/level/PathNavigationRegion.java -@@ -24,6 +24,7 @@ import net.minecraft.world.level.material.FluidState; - import net.minecraft.world.level.material.Fluids; - import net.minecraft.world.phys.AABB; - import net.minecraft.world.phys.shapes.VoxelShape; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - - public class PathNavigationRegion implements BlockGetter, CollisionGetter { - protected final int centerX; -@@ -35,7 +36,7 @@ public class PathNavigationRegion implements BlockGetter, CollisionGetter { - - public PathNavigationRegion(Level world, BlockPos minPos, BlockPos maxPos) { - this.level = world; -- this.plains = Suppliers.memoize(() -> { -+ this.plains = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier - return world.registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.PLAINS); - }); - this.centerX = SectionPos.blockToSectionCoord(minPos.getX()); -diff --git a/src/main/java/net/minecraft/world/level/biome/BiomeGenerationSettings.java b/src/main/java/net/minecraft/world/level/biome/BiomeGenerationSettings.java -index 3f9628ec396a11ffd49270be7996d13f606366b6..3f7e8bb767ba4e886549cf487a20f62c3519012a 100644 ---- a/src/main/java/net/minecraft/world/level/biome/BiomeGenerationSettings.java -+++ b/src/main/java/net/minecraft/world/level/biome/BiomeGenerationSettings.java -@@ -26,6 +26,7 @@ import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; - import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; - import net.minecraft.world.level.levelgen.feature.Feature; - import net.minecraft.world.level.levelgen.placement.PlacedFeature; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - import org.slf4j.Logger; - - public class BiomeGenerationSettings { -@@ -46,12 +47,12 @@ public class BiomeGenerationSettings { - BiomeGenerationSettings(Map>> carvers, List> features) { - this.carvers = carvers; - this.features = features; -- this.flowerFeatures = Suppliers.memoize(() -> { -+ this.flowerFeatures = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier - return features.stream().flatMap(HolderSet::stream).map(Holder::value).flatMap(PlacedFeature::getFeatures).filter((feature) -> { - return feature.feature() == Feature.FLOWER; - }).collect(ImmutableList.toImmutableList()); - }); -- this.featureSet = Suppliers.memoize(() -> { -+ this.featureSet = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier - return features.stream().flatMap(HolderSet::stream).map(Holder::value).collect(Collectors.toSet()); - }); - } -diff --git a/src/main/java/net/minecraft/world/level/block/WeatheringCopper.java b/src/main/java/net/minecraft/world/level/block/WeatheringCopper.java -index 16bd93606ac4a9c34108a92dd4b98cb4600221d9..f4aeb2a4cafe7fe0782660c0e7fd94ae216a8940 100644 ---- a/src/main/java/net/minecraft/world/level/block/WeatheringCopper.java -+++ b/src/main/java/net/minecraft/world/level/block/WeatheringCopper.java -@@ -6,12 +6,13 @@ import com.google.common.collect.ImmutableBiMap; - import java.util.Optional; - import java.util.function.Supplier; - import net.minecraft.world.level.block.state.BlockState; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - - public interface WeatheringCopper extends ChangeOverTimeBlock { -- Supplier> NEXT_BY_BLOCK = Suppliers.memoize(() -> { -+ Supplier> NEXT_BY_BLOCK = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier - return ImmutableBiMap.builder().put(Blocks.COPPER_BLOCK, Blocks.EXPOSED_COPPER).put(Blocks.EXPOSED_COPPER, Blocks.WEATHERED_COPPER).put(Blocks.WEATHERED_COPPER, Blocks.OXIDIZED_COPPER).put(Blocks.CUT_COPPER, Blocks.EXPOSED_CUT_COPPER).put(Blocks.EXPOSED_CUT_COPPER, Blocks.WEATHERED_CUT_COPPER).put(Blocks.WEATHERED_CUT_COPPER, Blocks.OXIDIZED_CUT_COPPER).put(Blocks.CUT_COPPER_SLAB, Blocks.EXPOSED_CUT_COPPER_SLAB).put(Blocks.EXPOSED_CUT_COPPER_SLAB, Blocks.WEATHERED_CUT_COPPER_SLAB).put(Blocks.WEATHERED_CUT_COPPER_SLAB, Blocks.OXIDIZED_CUT_COPPER_SLAB).put(Blocks.CUT_COPPER_STAIRS, Blocks.EXPOSED_CUT_COPPER_STAIRS).put(Blocks.EXPOSED_CUT_COPPER_STAIRS, Blocks.WEATHERED_CUT_COPPER_STAIRS).put(Blocks.WEATHERED_CUT_COPPER_STAIRS, Blocks.OXIDIZED_CUT_COPPER_STAIRS).build(); - }); -- Supplier> PREVIOUS_BY_BLOCK = Suppliers.memoize(() -> { -+ Supplier> PREVIOUS_BY_BLOCK = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier - return NEXT_BY_BLOCK.get().inverse(); - }); - -diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -index 42ecfc1237bfc724dc9134a6a8bae3670251449e..0a1ecc1a88fee4b68a69d95c417a663a320febca 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -@@ -76,6 +76,7 @@ import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStruct - import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; - import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - import org.apache.commons.lang3.mutable.MutableBoolean; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - - public abstract class ChunkGenerator { - -@@ -93,7 +94,7 @@ public abstract class ChunkGenerator { - public ChunkGenerator(BiomeSource biomeSource, Function, BiomeGenerationSettings> generationSettingsGetter) { - this.biomeSource = biomeSource; - this.generationSettingsGetter = generationSettingsGetter; -- this.featuresPerStep = Suppliers.memoize(() -> { -+ this.featuresPerStep = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier - return FeatureSorter.buildFeaturesPerStep(List.copyOf(biomeSource.possibleBiomes()), (holder) -> { - return ((BiomeGenerationSettings) generationSettingsGetter.apply(holder)).features(); - }, true); -diff --git a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -index 66205f6d99c629ce0e7e7d7b29394674bde115fb..14da533ccca35ba18190424c6d1074ce7e4de574 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -@@ -49,6 +49,7 @@ import net.minecraft.world.level.levelgen.blending.Blender; - import net.minecraft.world.level.levelgen.carver.CarvingContext; - import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; - import org.apache.commons.lang3.mutable.MutableObject; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - - public final class NoiseBasedChunkGenerator extends ChunkGenerator { - -@@ -67,7 +68,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { - public NoiseBasedChunkGenerator(BiomeSource biomeSource, Holder settings) { - super(biomeSource); - this.settings = settings; -- this.globalFluidPicker = Suppliers.memoize(() -> { -+ this.globalFluidPicker = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier - // Gale start - Lithium - cache world generator sea level - var fluidPicker = NoiseBasedChunkGenerator.createFluidPicker((NoiseGeneratorSettings) settings.value()); - this.cachedSeaLevel = settings.value().seaLevel(); -diff --git a/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java b/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java -index 45b0706a48c4bf44923a2590f34913b7373de8f4..5990287d2ffaa32accb2444d6627ed29dc65d60d 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java -@@ -28,6 +28,7 @@ import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.level.chunk.ChunkAccess; - import net.minecraft.world.level.levelgen.placement.CaveSurface; - import net.minecraft.world.level.levelgen.synth.NormalNoise; -+import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; - - public class SurfaceRules { - public static final SurfaceRules.ConditionSource ON_FLOOR = stoneDepthCheck(0, false, CaveSurface.FLOOR); -@@ -310,7 +311,7 @@ public class SurfaceRules { - - protected void updateY(int stoneDepthAbove, int stoneDepthBelow, int fluidHeight, int blockX, int blockY, int blockZ) { - ++this.lastUpdateY; -- this.biome = Suppliers.memoize(() -> { -+ this.biome = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier - return this.biomeGetter.apply(this.pos.set(blockX, blockY, blockZ)); - }); - this.blockY = blockY; -diff --git a/src/main/java/org/galemc/gale/executor/lock/YieldingMemoizedSupplier.java b/src/main/java/org/galemc/gale/executor/lock/YieldingMemoizedSupplier.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8732ea0a07a88117e0e6a2fe073a48bad4ff8b19 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/lock/YieldingMemoizedSupplier.java -@@ -0,0 +1,60 @@ -+// Gale - yielding memoized Supplier -+ -+package org.galemc.gale.executor.lock; -+ -+import com.google.common.base.Suppliers; -+import org.galemc.gale.concurrent.Mutex; -+import org.galemc.gale.concurrent.ThreadAwareNonReentrantLock; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.function.Supplier; -+ -+/** -+ * A replacement for {@link Suppliers#memoize} that provides thread-safety -+ * with a {@link YieldingLock} instead of by a synchronized block. -+ *
    -+ * The {@link #get()} method of this supplier must not be reachable from the body of the original supplier. -+ * To assure this, the lock used internally is a {@link ThreadAwareNonReentrantLock}. -+ *
    -+ * The original {@link Supplier} will be de-referenced once the value has been provided once, -+ * so that it may be garbage collected. -+ */ -+public class YieldingMemoizedSupplier implements Supplier { -+ -+ private volatile @Nullable Supplier delegate; -+ private volatile boolean initialized; -+ @Nullable T value; -+ private final YieldingLock lock = new MultipleWaitingBaseThreadsYieldingLock(new ThreadAwareNonReentrantLock(Mutex.createLock())); -+ -+ public YieldingMemoizedSupplier(Supplier delegate) { -+ this.delegate = delegate; -+ } -+ -+ @Override -+ public T get() { -+ if (!this.initialized) { -+ this.lock.lock(); -+ try { -+ if (!this.initialized) { -+ T t = this.delegate.get(); -+ this.value = t; -+ this.initialized = true; -+ this.delegate = null; -+ return t; -+ } -+ } finally { -+ this.lock.unlock(); -+ } -+ } -+ return value; -+ } -+ -+ @Override -+ public String toString() { -+ var delegate = this.delegate; -+ return this.getClass().getSimpleName() + "(" -+ + (delegate == null ? "" : delegate) -+ + ")"; -+ } -+ -+} diff --git a/patches/server/0155-Yielding-ChunkHolderManager.patch b/patches/server/0155-Yielding-ChunkHolderManager.patch deleted file mode 100644 index e2b72e0..0000000 --- a/patches/server/0155-Yielding-ChunkHolderManager.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Fri, 10 Feb 2023 20:56:11 +0100 -Subject: [PATCH] Yielding ChunkHolderManager - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java -index e5d9c6f2cbe11c2ded6d8ad111fa6a8b2086dfba..22720de9b94163b84dd6bf6ddf8b3e57eeb9389c 100644 ---- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java -+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java -@@ -36,6 +36,8 @@ import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.chunk.ChunkAccess; - import net.minecraft.world.level.chunk.ChunkStatus; - import org.bukkit.plugin.Plugin; -+import org.galemc.gale.executor.lock.MultipleWaitingBaseThreadsYieldingLock; -+import org.galemc.gale.executor.lock.YieldingLock; - import org.slf4j.Logger; - import java.io.IOException; - import java.text.DecimalFormat; -@@ -64,7 +66,7 @@ public final class ChunkHolderManager { - - private static final long NO_TIMEOUT_MARKER = -1L; - -- final ReentrantLock ticketLock = new ReentrantLock(); -+ final YieldingLock ticketLock = new MultipleWaitingBaseThreadsYieldingLock(new ReentrantLock()); // Gale - base thread pool - yielding ChunkHolderManager - - private final SWMRLong2ObjectHashTable chunkHolders = new SWMRLong2ObjectHashTable<>(16384, 0.25f); - private final Long2ObjectOpenHashMap>> tickets = new Long2ObjectOpenHashMap<>(8192, 0.25f); diff --git a/patches/server/0156-Run-async-executor-tasks-on-base-thread-pool.patch b/patches/server/0156-Run-async-executor-tasks-on-base-thread-pool.patch deleted file mode 100644 index bd41707..0000000 --- a/patches/server/0156-Run-async-executor-tasks-on-base-thread-pool.patch +++ /dev/null @@ -1,106 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Sun, 29 Jan 2023 23:52:19 +0100 -Subject: [PATCH] Run async executor tasks on base thread pool - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java -index 157f74bbbac8cbbed37c4fcc339b3b3c06d06bbe..cd614c55aa3f9a014c2a073abf803888e082c8d4 100644 ---- a/src/main/java/io/papermc/paper/util/MCUtil.java -+++ b/src/main/java/io/papermc/paper/util/MCUtil.java -@@ -58,14 +58,7 @@ import java.util.function.Consumer; - import java.util.function.Supplier; - - public final class MCUtil { -- public static final ThreadPoolExecutor asyncExecutor = new ThreadPoolExecutor( -- 0, 2, 60L, TimeUnit.SECONDS, -- new LinkedBlockingQueue<>(), -- new ThreadFactoryBuilder() -- .setNameFormat("Paper Async Task Handler Thread - %1$d") -- .setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(MinecraftServer.LOGGER)) -- .build() -- ); -+ public static final Executor asyncExecutor = BaseTaskQueues.scheduledAsync.yieldingExecutor; // Gale - base thread pool - remove Paper async executor - public static final ThreadPoolExecutor cleanerExecutor = new ThreadPoolExecutor( - 1, 1, 0L, TimeUnit.SECONDS, - new LinkedBlockingQueue<>(), -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index fe35347639364caf5edfca799f68fd4b41b9e913..d01c81bed1f77e7f8fec56b01fd7885a0603fd22 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1082,9 +1082,6 @@ public abstract class MinecraftServer extends MinecraftServerBlockableEventLoop - MinecraftServer.LOGGER.error("Failed to unlock level {}", this.storageSource.getLevelId(), ioexception1); - } - // Spigot start -- io.papermc.paper.util.MCUtil.asyncExecutor.shutdown(); // Paper -- try { io.papermc.paper.util.MCUtil.asyncExecutor.awaitTermination(30, java.util.concurrent.TimeUnit.SECONDS); // Paper -- } catch (java.lang.InterruptedException ignored) {} // Paper - if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) { - MinecraftServer.LOGGER.info("Saving usercache.json"); - this.getProfileCache().save(false); // Paper -@@ -1094,6 +1091,12 @@ public abstract class MinecraftServer extends MinecraftServerBlockableEventLoop - LOGGER.info("Flushing Chunk IO"); - io.papermc.paper.chunk.system.io.RegionFileIOThread.close(true); // Paper // Paper - rewrite chunk system - LOGGER.info("Closing Thread Pool"); -+ // Gale start - base thread pool - remove Paper async executor -+ long startTime = Util.getMillis(); -+ LOGGER.info("Waiting 30 seconds for asynchronous tasks to finish..."); -+ serverThread.runTasksUntil(null, () -> Util.getMillis() - startTime >= 30 || !BaseTaskQueues.scheduledAsync.hasTasks(), null); // Paper -+ LOGGER.info("Shutting down IO executor..."); -+ // Gale end - base thread pool - remove Paper async executor - Util.shutdownExecutors(); // Paper - LOGGER.info("Closing Server"); - try { -diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -index 7e24854f1e727e5e40108c68933466d0845bdca1..fb185e7f6344f21ed861e56c137ce470e891e766 100644 ---- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -@@ -75,7 +75,9 @@ public enum BaseTaskQueueTier { - * asynchronously with respect to the {@link ServerThread} and the ticking of the server. - * Execution of - */ -- ASYNC(new AbstractTaskQueue[0], Integer.getInteger("gale.thread.priority.async", 6)), -+ ASYNC(new AbstractTaskQueue[]{ -+ BaseTaskQueues.scheduledAsync -+ }, Integer.getInteger("gale.thread.priority.async", 6)), - /** - * A tier for queues that contain tasks with the same considerations as {@link #ASYNC}, - * but with a low priority. -diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -index cf8e2b42ecfc8205af5b105e19975c3e54ffec5f..2295ead9ddcb57be81f8b8bd0731f56c9f7f60b9 100644 ---- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -@@ -54,7 +54,7 @@ public final class BaseTaskQueues { - /** - * This queue explicitly stores tasks that represent steps or parts of steps in ticking the server and that must be - * executed on the main thread, and as such always have a higher priority in being started than pending tasks in -- * {@link #anyTickScheduledServerThread}. -+ * {@link #anyTickScheduledServerThread} and {@link #scheduledAsync}. - *
    - * This queue may contain tasks of every {@link TaskSpan}. - *
    -@@ -79,7 +79,8 @@ public final class BaseTaskQueues { - - /** - * This queue explicitly stores tasks that represent steps or parts of steps in ticking the server that do not have -- * to be executed on the main thread (but must be executed on a {@link BaseThread}). -+ * to be executed on the main thread (but must be executed on a {@link BaseThread}), and have a higher priority -+ * in being started than pending tasks in {@link #scheduledAsync}. - *
    - * This queue may contain tasks of every {@link TaskSpan}. - *
    -@@ -89,4 +90,12 @@ public final class BaseTaskQueues { - */ - public static final SimpleTaskQueue tickAssist = SimpleTaskQueue.allSpans("TickAssist"); - -+ /** -+ * This queue stores the tasks scheduled to be executed on any thread, which would usually be stored in various -+ * executors with a specific purpose. -+ *
    -+ * This queue may contain tasks of every {@link TaskSpan}. -+ */ -+ public static final SimpleTaskQueue scheduledAsync = SimpleTaskQueue.allSpans("ScheduledAsync"); -+ - } diff --git a/patches/server/0157-Run-background-executor-tasks-on-base-thread-pool.patch b/patches/server/0157-Run-background-executor-tasks-on-base-thread-pool.patch deleted file mode 100644 index afba135..0000000 --- a/patches/server/0157-Run-background-executor-tasks-on-base-thread-pool.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Sun, 29 Jan 2023 23:56:57 +0100 -Subject: [PATCH] Run background executor tasks on base thread pool - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java -index 4c90a6ee734451fe404b2255d21090b669da1aa1..0ddacb31f037feac052c5019f19bea49b43af942 100644 ---- a/src/main/java/net/minecraft/Util.java -+++ b/src/main/java/net/minecraft/Util.java -@@ -26,9 +26,6 @@ import java.net.URL; - import java.nio.file.Files; - import java.nio.file.Path; - import java.nio.file.spi.FileSystemProvider; --import java.security.AccessController; --import java.security.PrivilegedActionException; --import java.security.PrivilegedExceptionAction; - import java.time.Duration; - import java.time.Instant; - import java.time.ZonedDateTime; -@@ -47,8 +44,6 @@ import java.util.concurrent.ConcurrentHashMap; - import java.util.concurrent.Executor; - import java.util.concurrent.ExecutorService; - import java.util.concurrent.Executors; --import java.util.concurrent.ForkJoinPool; --import java.util.concurrent.ForkJoinWorkerThread; - import java.util.concurrent.LinkedBlockingQueue; - import java.util.concurrent.TimeUnit; - import java.util.concurrent.atomic.AtomicInteger; -@@ -67,12 +62,12 @@ import java.util.stream.Stream; - import javax.annotation.Nullable; - import net.minecraft.resources.ResourceLocation; - import net.minecraft.server.Bootstrap; --import net.minecraft.util.Mth; - import net.minecraft.util.RandomSource; - import net.minecraft.util.SingleKeyCache; - import net.minecraft.util.TimeSource; - import net.minecraft.util.datafix.DataFixers; - import net.minecraft.world.level.block.state.properties.Property; -+import org.galemc.gale.executor.queue.BaseTaskQueues; - import org.slf4j.Logger; - - public class Util { -@@ -80,7 +75,7 @@ public class Util { - private static final int DEFAULT_MAX_THREADS = 255; - private static final String MAX_THREADS_SYSTEM_PROPERTY = "max.bg.threads"; - private static final AtomicInteger WORKER_COUNT = new AtomicInteger(1); -- private static final ExecutorService BACKGROUND_EXECUTOR = makeExecutor("Main", -1); // Paper - add -1 priority -+ private static final ExecutorService BACKGROUND_EXECUTOR = BaseTaskQueues.scheduledAsync.yieldingExecutor; // Gale - base thread pool - remove background executor - // Paper start - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread - public static final ExecutorService PROFILE_EXECUTOR = Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { - -@@ -215,7 +210,6 @@ public class Util { - } - - public static void shutdownExecutors() { -- shutdownExecutor(BACKGROUND_EXECUTOR); - shutdownExecutor(IO_POOL); - } - diff --git a/patches/server/0158-Run-world-upgrade-tasks-on-base-thread-pool.patch b/patches/server/0158-Run-world-upgrade-tasks-on-base-thread-pool.patch deleted file mode 100644 index 22e6403..0000000 --- a/patches/server/0158-Run-world-upgrade-tasks-on-base-thread-pool.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Mon, 30 Jan 2023 00:08:14 +0100 -Subject: [PATCH] Run world upgrade tasks on base thread pool - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java -index c13df3a375f416273c6a26f5f77624c1f34a918c..ccab7e0d529a9909410da4301db037bf4b0f02f0 100644 ---- a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java -+++ b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java -@@ -7,17 +7,16 @@ import net.minecraft.nbt.CompoundTag; - import net.minecraft.resources.ResourceKey; - import net.minecraft.util.worldupdate.WorldUpgrader; - import net.minecraft.world.level.ChunkPos; --import net.minecraft.world.level.Level; - import net.minecraft.world.level.chunk.ChunkGenerator; - import net.minecraft.world.level.chunk.storage.ChunkStorage; - import net.minecraft.world.level.chunk.storage.RegionFileStorage; --import net.minecraft.world.level.dimension.DimensionType; - import net.minecraft.world.level.dimension.LevelStem; --import net.minecraft.world.level.levelgen.WorldGenSettings; - import net.minecraft.world.level.storage.DimensionDataStorage; - import net.minecraft.world.level.storage.LevelStorageSource; - import org.apache.logging.log4j.LogManager; - import org.apache.logging.log4j.Logger; -+import org.galemc.gale.executor.queue.BaseTaskQueues; -+ - import java.io.File; - import java.io.IOException; - import java.text.DecimalFormat; -@@ -47,6 +46,10 @@ public class ThreadedWorldUpgrader { - this.dimensionType = dimensionType; - this.worldName = worldName; - this.worldDir = worldDir; -+ // Gale start - base thread pool - remove world upgrade executors -+ this.threadPool = BaseTaskQueues.scheduledAsync.yieldingExecutor; -+ /* -+ // Gale end - base thread pool - remove world upgrade executors - this.threadPool = Executors.newFixedThreadPool(Math.max(1, threads), new ThreadFactory() { - private final AtomicInteger threadCounter = new AtomicInteger(); - -@@ -62,6 +65,7 @@ public class ThreadedWorldUpgrader { - return ret; - } - }); -+ */ // Gale - base thread pool - remove world upgrade executors - this.dataFixer = dataFixer; - this.generatorKey = generatorKey; - this.removeCaches = removeCaches; -@@ -121,17 +125,22 @@ public class ThreadedWorldUpgrader { - // Gale end - instantly continue on world upgrade finish - } - finalExpectedChunks[0] = expectedChunks; // Gale - instantly continue on world upgrade finish -- this.threadPool.shutdown(); - - final DecimalFormat format = new DecimalFormat("#0.00"); - - final long start = System.nanoTime(); - -- while (!this.threadPool.isTerminated()) { -+ while (true) { // Gale - base thread pool - remove world upgrade executors - wait for task completion instead of thread pool termination - final long current = info.convertedChunks.get(); - - LOGGER.info("{}% completed ({} / {} chunks)...", format.format((double)current / (double)expectedChunks * 100.0), current, expectedChunks); - -+ // Gale start - base thread pool - remove world upgrade executors - wait for task completion instead of thread pool termination -+ if (current == expectedChunks) { -+ break; -+ } -+ // Gale end - base thread pool - remove world upgrade executors - wait for task completion instead of thread pool termination -+ - // Gale start - instantly continue on world upgrade finish - while (!finishLock.tryLock()) { - Thread.onSpinWait(); diff --git a/patches/server/0159-Run-tab-completion-tasks-on-base-thread-pool.patch b/patches/server/0159-Run-tab-completion-tasks-on-base-thread-pool.patch deleted file mode 100644 index cae21fd..0000000 --- a/patches/server/0159-Run-tab-completion-tasks-on-base-thread-pool.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Mon, 30 Jan 2023 00:11:30 +0100 -Subject: [PATCH] Run tab completion tasks on base thread pool - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 7c3cc5cdcf352f50eab0dda110399a60a758e269..f5f9b500d3c0221ef2eb5d2f1a5321d0ace0bb95 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -896,8 +896,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - } - - // Paper start -- private static final java.util.concurrent.ExecutorService TAB_COMPLETE_EXECUTOR = java.util.concurrent.Executors.newFixedThreadPool(4, -- new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon(true).setNameFormat("Async Tab Complete Thread - #%d").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build()); -+ private static final java.util.concurrent.ExecutorService TAB_COMPLETE_EXECUTOR = BaseTaskQueues.scheduledAsync.yieldingExecutor; // Gale - base thread pool - remove tab complete executor - // Paper end - @Override - public void handleCustomCommandSuggestions(ServerboundCommandSuggestionPacket packet) { diff --git a/patches/server/0160-Run-text-filter-tasks-on-base-thread-pool.patch b/patches/server/0160-Run-text-filter-tasks-on-base-thread-pool.patch deleted file mode 100644 index 70bed7d..0000000 --- a/patches/server/0160-Run-text-filter-tasks-on-base-thread-pool.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Mon, 30 Jan 2023 00:15:33 +0100 -Subject: [PATCH] Run text filter tasks on base thread pool - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/net/minecraft/server/network/TextFilterClient.java b/src/main/java/net/minecraft/server/network/TextFilterClient.java -index 92f56e29ffb7386cbbf7893c000ff330eb997ba5..3a0f656eafa0411b35bbf20fcb4f9fcff5b7184c 100644 ---- a/src/main/java/net/minecraft/server/network/TextFilterClient.java -+++ b/src/main/java/net/minecraft/server/network/TextFilterClient.java -@@ -23,7 +23,6 @@ import java.util.List; - import java.util.concurrent.CompletableFuture; - import java.util.concurrent.Executor; - import java.util.concurrent.ExecutorService; --import java.util.concurrent.Executors; - import java.util.concurrent.ThreadFactory; - import java.util.concurrent.atomic.AtomicInteger; - import javax.annotation.Nullable; -@@ -32,6 +31,7 @@ import net.minecraft.Util; - import net.minecraft.network.chat.FilterMask; - import net.minecraft.util.GsonHelper; - import net.minecraft.util.thread.ProcessorMailbox; -+import org.galemc.gale.executor.queue.BaseTaskQueues; - import org.slf4j.Logger; - - public class TextFilterClient implements AutoCloseable { -@@ -62,7 +62,7 @@ public class TextFilterClient implements AutoCloseable { - this.joinEncoder = joinEncoder; - this.leaveEndpoint = leaveEndpoint; - this.leaveEncoder = leaveEncoder; -- this.workerPool = Executors.newFixedThreadPool(parallelism, THREAD_FACTORY); -+ this.workerPool = BaseTaskQueues.scheduledAsync.yieldingExecutor; // Gale - base thread pool - remove text filter executor - } - - private static URL getEndpoint(URI root, @Nullable JsonObject endpoints, String key, String fallback) throws MalformedURLException { diff --git a/patches/server/0161-Run-cleaner-tasks-on-base-thread-pool.patch b/patches/server/0161-Run-cleaner-tasks-on-base-thread-pool.patch deleted file mode 100644 index df63789..0000000 --- a/patches/server/0161-Run-cleaner-tasks-on-base-thread-pool.patch +++ /dev/null @@ -1,102 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Sun, 29 Jan 2023 23:25:41 +0100 -Subject: [PATCH] Run cleaner tasks on base thread pool - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java -index cd614c55aa3f9a014c2a073abf803888e082c8d4..2f1a8580a9b2321ec736008ade93d74c13f0392c 100644 ---- a/src/main/java/io/papermc/paper/util/MCUtil.java -+++ b/src/main/java/io/papermc/paper/util/MCUtil.java -@@ -8,7 +8,6 @@ import com.google.gson.JsonArray; - import com.google.gson.JsonObject; - import com.google.gson.internal.Streams; - import com.google.gson.stream.JsonWriter; --import com.mojang.datafixers.util.Either; - import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; - import java.lang.ref.Cleaner; - import it.unimi.dsi.fastutil.objects.ReferenceArrayList; -@@ -18,16 +17,11 @@ import net.minecraft.server.MinecraftServer; - import net.minecraft.nbt.CompoundTag; - import net.minecraft.network.chat.Component; - import net.minecraft.server.level.ChunkHolder; --import net.minecraft.server.level.ChunkMap; --import net.minecraft.server.level.DistanceManager; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.server.level.ServerPlayer; --import net.minecraft.server.level.Ticket; - import net.minecraft.world.entity.Entity; - import net.minecraft.world.level.ChunkPos; --import net.minecraft.world.level.ClipContext; - import net.minecraft.world.level.Level; --import net.minecraft.world.level.chunk.ChunkAccess; - import net.minecraft.world.level.chunk.ChunkStatus; - import org.apache.commons.lang.exception.ExceptionUtils; - import com.mojang.authlib.GameProfile; -@@ -35,7 +29,6 @@ import org.bukkit.Location; - import org.bukkit.block.BlockFace; - import org.bukkit.craftbukkit.CraftWorld; - import org.bukkit.craftbukkit.util.Waitable; --import org.spigotmc.AsyncCatcher; - import org.galemc.gale.executor.queue.BaseTaskQueues; - - import javax.annotation.Nonnull; -@@ -44,11 +37,8 @@ import java.io.*; - import java.nio.charset.StandardCharsets; - import java.util.List; - import java.util.Queue; --import java.util.Set; - import java.util.concurrent.CompletableFuture; - import java.util.concurrent.ExecutionException; --import java.util.concurrent.LinkedBlockingQueue; --import java.util.concurrent.ThreadPoolExecutor; - import java.util.concurrent.Executor; - import java.util.concurrent.TimeUnit; - import java.util.concurrent.TimeoutException; -@@ -59,14 +49,7 @@ import java.util.function.Supplier; - - public final class MCUtil { - public static final Executor asyncExecutor = BaseTaskQueues.scheduledAsync.yieldingExecutor; // Gale - base thread pool - remove Paper async executor -- public static final ThreadPoolExecutor cleanerExecutor = new ThreadPoolExecutor( -- 1, 1, 0L, TimeUnit.SECONDS, -- new LinkedBlockingQueue<>(), -- new ThreadFactoryBuilder() -- .setNameFormat("Paper Object Cleaner") -- .setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(MinecraftServer.LOGGER)) -- .build() -- ); -+ public static final Executor cleanerExecutor = BaseTaskQueues.cleaner.executor; // Gale - base thread pool - remove Paper cleaner executor - - public static final long INVALID_CHUNK_KEY = getCoordinateKey(Integer.MAX_VALUE, Integer.MAX_VALUE); - -diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -index fb185e7f6344f21ed861e56c137ce470e891e766..f4adcdcad96b2748c60aecb8f5c25370ee6e8f5b 100644 ---- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -@@ -76,6 +76,8 @@ public enum BaseTaskQueueTier { - * Execution of - */ - ASYNC(new AbstractTaskQueue[]{ -+ // The cleaner queue has high priority because it releases resources back to a pool, thereby saving memory -+ BaseTaskQueues.cleaner, - BaseTaskQueues.scheduledAsync - }, Integer.getInteger("gale.thread.priority.async", 6)), - /** -diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -index 2295ead9ddcb57be81f8b8bd0731f56c9f7f60b9..92721a51268becb05d708db04e9d6daaa66fb8b2 100644 ---- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -@@ -90,6 +90,11 @@ public final class BaseTaskQueues { - */ - public static final SimpleTaskQueue tickAssist = SimpleTaskQueue.allSpans("TickAssist"); - -+ /** -+ * This queue stores the tasks posted to {@link MCUtil#cleanerExecutor}. -+ */ -+ public static final SingleSpanSimpleTaskQueue cleaner = SimpleTaskQueue.singleSpan("Cleaner", TaskSpan.TINY); -+ - /** - * This queue stores the tasks scheduled to be executed on any thread, which would usually be stored in various - * executors with a specific purpose. diff --git a/patches/server/0162-Run-chunk-cache-tasks-on-base-thread-pool.patch b/patches/server/0162-Run-chunk-cache-tasks-on-base-thread-pool.patch deleted file mode 100644 index 1d2c817..0000000 --- a/patches/server/0162-Run-chunk-cache-tasks-on-base-thread-pool.patch +++ /dev/null @@ -1,1076 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Sun, 29 Jan 2023 22:37:12 +0100 -Subject: [PATCH] Run chunk cache tasks on base thread pool - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/com/destroystokyo/paper/util/misc/DistanceTrackingAreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/DistanceTrackingAreaMap.java -index 896c3ff7ddb07f1f6f05f90e1e3fe7fb615071d4..1dfee2b857f2a37fa1bb9b8e163809963b408613 100644 ---- a/src/main/java/com/destroystokyo/paper/util/misc/DistanceTrackingAreaMap.java -+++ b/src/main/java/com/destroystokyo/paper/util/misc/DistanceTrackingAreaMap.java -@@ -15,7 +15,7 @@ public abstract class DistanceTrackingAreaMap extends AreaMap { - this.chunkToNearestDistance.defaultReturnValue(-1); - } - -- protected final DistanceChangeCallback distanceChangeCallback; -+ public DistanceChangeCallback distanceChangeCallback; // Gale - base thread pool - chunk-sorted cache tasks - private -> public, final -> non-final - - public DistanceTrackingAreaMap() { - this(new PooledLinkedHashSets<>()); -diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java -index e77972c4c264100ffdd824bfa2dac58dbbc6d678..b2e4fb69fd6564484e0ebd120ba87431c5c158e4 100644 ---- a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java -+++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java -@@ -718,7 +718,7 @@ public final class PlayerChunkLoader { - this.onChunkSendReady(queuedLoad.chunkX, queuedLoad.chunkZ); - } else if (this.chunkNeedsPostProcessing(queuedLoad.chunkX, queuedLoad.chunkZ)) { - // requires post processing -- this.chunkMap.mainThreadExecutor.execute(() -> { -+ this.chunkMap.mainThreadExecutor.execute(queuedLoad.chunkX, queuedLoad.chunkZ, () -> { // Gale - base thread pool - chunk-sorted cache tasks - final long key = CoordinateUtils.getChunkKey(queuedLoad.chunkX, queuedLoad.chunkZ); - final ChunkHolder holder = PlayerChunkLoader.this.chunkMap.getVisibleChunkIfPresent(key); - -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 0a39aaf5bc54e1fb7fb4bada2a15fea9b73af731..d7bab50d51896c6aeb6015d5c2eb130c88338ede 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -1,6 +1,7 @@ - package net.minecraft.server.level; - - import co.aikar.timings.Timing; // Paper -+import com.destroystokyo.paper.util.misc.PooledLinkedHashSets; - import com.google.common.collect.ImmutableList; - import com.google.common.collect.ImmutableList.Builder; - import com.google.common.collect.Iterables; -@@ -111,6 +112,7 @@ import net.minecraft.world.level.storage.LevelStorageSource; - import net.minecraft.world.phys.Vec3; - import org.apache.commons.lang3.mutable.MutableBoolean; - import org.apache.commons.lang3.mutable.MutableObject; -+import org.galemc.gale.executor.ClosestChunkBlockableEventLoop; - import org.slf4j.Logger; - import org.bukkit.craftbukkit.generator.CustomChunkGenerator; - import org.bukkit.entity.Player; -@@ -134,7 +136,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - // Paper - rewrite chunk system - public final ServerLevel level; - private final ThreadedLevelLightEngine lightEngine; -- public final BlockableEventLoop mainThreadExecutor; // Paper - public -+ public final ClosestChunkBlockableEventLoop mainThreadExecutor; // Paper - public // Gale - base thread pool - chunk-sorted cache tasks - public ChunkGenerator generator; - private final RandomState randomState; - private final ChunkGeneratorStructureState chunkGeneratorState; -@@ -178,7 +180,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - - // these maps are named after spigot's uses - public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap; // this map is absent from updateMaps since it's controlled at the start of the chunkproviderserver tick -- public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerChunkTickRangeMap; -+ public final com.destroystokyo.paper.util.misc.PlayerDistanceTrackingAreaMap playerChunkTickRangeMap; // Gale - base thread pool - chunk-sorted cache tasks - // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning - // Paper start - use distance map to optimise tracker - public static boolean isLegacyTrackingEntity(Entity entity) { -@@ -293,7 +295,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - // Paper end - -- public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { -+ public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, ClosestChunkBlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { - super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); - // Paper - rewrite chunk system - this.tickingGenerated = new AtomicInteger(); -@@ -325,7 +327,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - - this.chunkGeneratorState = chunkGenerator.createState(iregistrycustom.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, j, world.spigotConfig); // Spigot -- this.mainThreadExecutor = mainThreadExecutor; -+ this.mainThreadExecutor = mainThreadExecutor; // Gale - base thread pool - // Paper - rewrite chunk system - - Objects.requireNonNull(mainThreadExecutor); -@@ -386,7 +388,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - // Paper end - use distance map to optimise entity tracker - // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning -- this.playerChunkTickRangeMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, -+ this.playerChunkTickRangeMap = new com.destroystokyo.paper.util.misc.PlayerDistanceTrackingAreaMap(this.pooledLinkedPlayerHashSets, // Gale - base thread pool - chunk-sorted cache tasks - (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, - com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { - ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ)); -@@ -400,7 +402,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - if (playerChunk != null) { - playerChunk.playersInChunkTickRange = newState; - } -+ // Gale start - base thread pool - chunk-sorted cache tasks -+ }, -+ (int posX, int posZ, int oldNearestDistance, int newNearestDistance, PooledLinkedHashSets.PooledObjectLinkedOpenHashSet state) -> { -+ this.level.chunkSource.mainThreadProcessor.onChunkDistanceChange(posX, posZ, newNearestDistance); - }); -+ // Gale end - base thread pool - chunk-sorted cache tasks - this.playerMobSpawnMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, - (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, - com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { -@@ -696,7 +703,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - - protected void releaseLightTicket(ChunkPos pos) { -- this.mainThreadExecutor.tell(Util.name(() -> { -+ this.mainThreadExecutor.tell(pos.x, pos.z, Util.name(() -> { // Gale - base thread pool - chunk-sorted cache tasks - this.distanceManager.removeTicket(TicketType.LIGHT, pos, 33 + ChunkStatus.getDistance(ChunkStatus.LIGHT), pos); - }, () -> { - return "release light ticket " + pos; -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 83a57b9bc59063ed8299f98bc33e14b57f2ea0de..9ee931a6442d3f18a7521704f39297af0d7af6d7 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -2,16 +2,12 @@ package net.minecraft.server.level; - - import com.google.common.annotations.VisibleForTesting; - import com.google.common.collect.Lists; -+import com.google.common.collect.Queues; - import com.mojang.datafixers.DataFixer; - import com.mojang.datafixers.util.Either; - import java.io.File; - import java.io.IOException; --import java.util.Arrays; --import java.util.Collections; --import java.util.Iterator; --import java.util.List; --import java.util.Objects; --import java.util.Optional; -+import java.util.*; - import java.util.concurrent.CompletableFuture; - import java.util.concurrent.Executor; - import java.util.function.BooleanSupplier; -@@ -22,6 +18,7 @@ import net.minecraft.Util; - import net.minecraft.core.BlockPos; - import net.minecraft.core.SectionPos; - import net.minecraft.network.protocol.Packet; -+import net.minecraft.server.MinecraftServer; - import net.minecraft.server.level.progress.ChunkProgressListener; - import net.minecraft.util.VisibleForDebug; - import net.minecraft.util.thread.BlockableEventLoop; -@@ -48,6 +45,7 @@ import net.minecraft.world.level.storage.DimensionDataStorage; - import net.minecraft.world.level.storage.LevelData; - import net.minecraft.world.level.storage.LevelStorageSource; - import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper -+import org.galemc.gale.executor.ClosestChunkBlockableEventLoop; - import org.galemc.gale.executor.lock.YieldingLock; - import org.galemc.gale.executor.queue.BaseTaskQueues; - import org.galemc.gale.executor.thread.AbstractYieldingThread; -@@ -308,6 +306,7 @@ public class ServerChunkCache extends ChunkSource { - file.mkdirs(); - this.dataStorage = new DimensionDataStorage(file, dataFixer); - this.chunkMap = new ChunkMap(world, session, dataFixer, structureTemplateManager, workerExecutor, this.mainThreadProcessor, this, chunkGenerator, worldGenerationProgressListener, chunkStatusChangeListener, persistentStateManagerFactory, viewDistance, dsync); -+ this.mainThreadProcessor.setAreaMap(this.chunkMap.playerChunkTickRangeMap); // Gale - base thread pool - chunk-sorted cache tasks - this.lightEngine = this.chunkMap.getLightEngine(); - this.distanceManager = this.chunkMap.getDistanceManager(); - this.distanceManager.updateSimulationDistance(simulationDistance); -@@ -422,7 +421,7 @@ public class ServerChunkCache extends ChunkSource { - if (!io.papermc.paper.util.TickThread.isTickThread()) { // Paper - rewrite chunk system - return (ChunkAccess) CompletableFuture.supplyAsync(() -> { - return this.getChunk(x, z, leastStatus, create); -- }, this.mainThreadProcessor).join(); -+ }, this.mainThreadProcessor.createExecutorForChunk(x, z)).join(); // Gale - base thread pool - chunk-sorted cache tasks - } else { - // Paper start - optimise for loaded chunks - LevelChunk ifLoaded = this.getChunkAtIfLoadedMainThread(x, z); -@@ -446,7 +445,7 @@ public class ServerChunkCache extends ChunkSource { - // Paper end - com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info - this.level.timings.syncChunkLoad.startTiming(); // Paper -- chunkproviderserver_b.managedBlock(completablefuture::isDone); -+ chunkproviderserver_b.managedYield(completablefuture); // Gale - base thread pool - io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system - this.level.timings.syncChunkLoad.stopTiming(); // Paper - } // Paper -@@ -489,11 +488,11 @@ public class ServerChunkCache extends ChunkSource { - ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor; - - Objects.requireNonNull(completablefuture); -- chunkproviderserver_b.managedBlock(completablefuture::isDone); -+ chunkproviderserver_b.managedYield(completablefuture); // Gale - base thread pool - } else { - completablefuture = CompletableFuture.supplyAsync(() -> { - return this.getChunkFutureMainThread(chunkX, chunkZ, leastStatus, create); -- }, this.mainThreadProcessor).thenCompose((completablefuture1) -> { -+ }, this.mainThreadProcessor.createExecutorForChunk(chunkX, chunkZ)).thenCompose((completablefuture1) -> { // Gale - base thread pool - chunk-sorted cache tasks - return completablefuture1; - }); - } -@@ -885,7 +884,7 @@ public class ServerChunkCache extends ChunkSource { - - @Override - public void onLightUpdate(LightLayer type, SectionPos pos) { -- this.mainThreadProcessor.execute(() -> { -+ this.mainThreadProcessor.execute(pos.x(), pos.z(), () -> { // Gale - base thread pool - chunk-sorted cache tasks - ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos.chunk().toLong()); - - if (playerchunk != null) { -@@ -971,7 +970,16 @@ public class ServerChunkCache extends ChunkSource { - this.distanceManager.removeTicketsOnClosing(); - } - -- public final class MainThreadExecutor extends BlockableEventLoop { -+ // Gale start - base thread pool -+ public final class MainThreadExecutor extends ClosestChunkBlockableEventLoop { // Gale - base thread pool - chunk-sorted cache tasks -+ -+ /** -+ * The time interval for the server thread to yield when this executor is performing -+ * a {@link #managedYield} but failed to perform any other tasks from this executor itself. -+ */ -+ private static final long MANAGED_YIELD_TIMEOUT_TIME = 50_000L; -+ private static @Nullable YieldingLock yieldingLockToNotifyForNewChunkCacheTasks; -+ // Gale end - base thread pool - - MainThreadExecutor(Level world) { - super("Chunk source main thread executor for " + world.dimension().location()); -@@ -1002,6 +1010,40 @@ public class ServerChunkCache extends ChunkSource { - super.doRunTask(task); - } - -+ // Gale start - base thread pool -+ @Override -+ public void tell(int chunkX, int chunkZ, Runnable runnable) { -+ super.tell(chunkX, chunkZ, runnable); -+ MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks = true; -+ BaseTaskQueues.allLevelsScheduledChunkCache.newTaskWasAdded(); -+ if (yieldingLockToNotifyForNewChunkCacheTasks != null) { -+ yieldingLockToNotifyForNewChunkCacheTasks.unlock(); -+ } -+ } -+ -+ public void managedBlock(BooleanSupplier stopCondition) { -+ throw new UnsupportedOperationException("Cannot call " + this.getClass().getName() + ".managedBlock(BooleanSupplier), call managedYield(CompletableFuture) instead"); -+ } -+ -+ public void managedYield(CompletableFuture future) { -+ if (!future.isDone()) { -+ ++this.blockingCount; -+ try { -+ var currentThread = AbstractYieldingThread.currentYieldingThread(); -+ while (!future.isDone()) { -+ if (!this.pollTask()) { -+ long timeoutTime = System.nanoTime() + MANAGED_YIELD_TIMEOUT_TIME; -+ currentThread.yieldUntilFuture(timeoutTime, () -> this.hasPendingTasks(), future, autoCompletingLock -> yieldingLockToNotifyForNewChunkCacheTasks = autoCompletingLock); -+ } -+ } -+ yieldingLockToNotifyForNewChunkCacheTasks = null; -+ } finally { -+ --this.blockingCount; -+ } -+ } -+ } -+ // Gale end - base thread pool -+ - @Override - // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task - public boolean pollTask() { -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index feeca393f960210a62740da053d21fecd62b0252..8a6f903d84eb9259236a63011d66b7246fca28c2 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -275,7 +275,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - public final void loadChunksForMoveAsync(AABB axisalignedbb, ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority priority, - java.util.function.Consumer> onLoad) { - if (Thread.currentThread() != this.thread) { -- this.getChunkSource().mainThreadProcessor.execute(() -> { -+ this.getChunkSource().mainThreadProcessor.execute(Mth.floor((axisalignedbb.minX + axisalignedbb.maxX) / 2.0) >> 4, Mth.floor((axisalignedbb.minZ + axisalignedbb.maxZ) / 2.0) >> 4, () -> { // Gale - base thread pool - chunk-sorted cache tasks - this.loadChunksForMoveAsync(axisalignedbb, priority, onLoad); - }); - return; -diff --git a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java -index 660693c6dc0ef86f4013df980b6d0c11c03e46cd..236cc920a5943abb249d50a6957d6418fd941501 100644 ---- a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java -+++ b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java -@@ -98,7 +98,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl - this.chunkMap.level.chunkTaskScheduler.lightExecutor.queueRunnable(() -> { // Paper - rewrite chunk system - this.theLightEngine.relightChunks(chunks, (ChunkPos chunkPos) -> { - chunkLightCallback.accept(chunkPos); -- ((java.util.concurrent.Executor)((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().mainThreadProcessor).execute(() -> { -+ (((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().mainThreadProcessor).execute(chunkPos.x, chunkPos.z, () -> { // Gale - base thread pool - chunk-sorted cache tasks - ((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().chunkMap.getUpdatingChunkIfPresent(chunkPos.toLong()).broadcast(new net.minecraft.network.protocol.game.ClientboundLightUpdatePacket(chunkPos, ThreadedLevelLightEngine.this, null, null, true), false); - ((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().removeTicketAtLevel(TicketType.CHUNK_RELIGHT, chunkPos, io.papermc.paper.util.MCUtil.getTicketLevelFor(ChunkStatus.LIGHT), ticketIds.get(chunkPos)); - }); -@@ -130,7 +130,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl - - if (!world.getChunkSource().chunkMap.mainThreadExecutor.isSameThread()) { - // ticket logic is not safe to run off-main, re-schedule -- world.getChunkSource().chunkMap.mainThreadExecutor.execute(() -> { -+ world.getChunkSource().chunkMap.mainThreadExecutor.execute(chunkX, chunkZ, () -> { // Gale - base thread pool - chunk-sorted cache tasks - this.queueTaskForSection(chunkX, chunkY, chunkZ, runnable); - }); - return; -@@ -160,7 +160,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl - } else { - this.chunksBeingWorkedOn.put(key, newReferences - 1); - } -- }, world.getChunkSource().chunkMap.mainThreadExecutor).whenComplete((final Void ignore, final Throwable thr) -> { -+ }, world.getChunkSource().chunkMap.mainThreadExecutor.createExecutorForChunk(chunkX, chunkZ)).whenComplete((final Void ignore, final Throwable thr) -> { // Gale - base thread pool - chunk-sorted cache tasks - if (thr != null) { - LOGGER.error("Failed to remove ticket level for post chunk task " + new ChunkPos(chunkX, chunkZ), thr); - } -diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -index 392e7b4a89669f16b32043b65b69e6593d17f10e..c2378d66bbd65f786a942eba74dd374b551bcbe8 100644 ---- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -+++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -@@ -21,7 +21,7 @@ public abstract class BlockableEventLoop implements Profiler - private final String name; - private static final Logger LOGGER = LogUtils.getLogger(); - private final Queue pendingRunnables = Queues.newConcurrentLinkedQueue(); -- private int blockingCount; -+ protected int blockingCount; // Gale - base thread pool - - protected BlockableEventLoop(String name) { - this.name = name; -@@ -62,7 +62,7 @@ public abstract class BlockableEventLoop implements Profiler - return this.scheduleExecutables() ? CompletableFuture.supplyAsync(task, this) : CompletableFuture.completedFuture(task.get()); - } - -- private CompletableFuture submitAsync(Runnable runnable) { -+ protected CompletableFuture submitAsync(Runnable runnable) { // Gale - base thread pool - private -> protected - return CompletableFuture.supplyAsync(() -> { - runnable.run(); - return null; -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 18c3f13c523ea62a098f489636b4320c73da4b8b..43c3ff92bad2b812fbad9af65bde17681690a72e 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -196,7 +196,7 @@ public class LevelChunk extends ChunkAccess { - if (!areNeighboursLoaded(bitsetBefore, 2) && areNeighboursLoaded(bitsetAfter, 2)) { - if (chunkMap.playerChunkManager.tickMap.getObjectsInRange(this.coordinateKey) != null) { // Paper - replace old player chunk loading system - // now we're ready for entity ticking -- chunkProviderServer.mainThreadProcessor.execute(() -> { -+ chunkProviderServer.mainThreadProcessor.execute(this.chunkPos.x, this.chunkPos.z, () -> { // Gale - base thread pool - chunk-sorted cache tasks - // double check that this condition still holds. - if (LevelChunk.this.areNeighboursLoaded(2) && chunkMap.playerChunkManager.tickMap.getObjectsInRange(LevelChunk.this.coordinateKey) != null) { // Paper - replace old player chunk loading system - chunkMap.playerChunkManager.onChunkPlayerTickReady(this.chunkPos.x, this.chunkPos.z); // Paper - replace old player chunk -@@ -212,7 +212,7 @@ public class LevelChunk extends ChunkAccess { - if (chunkMap.playerChunkManager.isChunkNearPlayers(this.chunkPos.x, this.chunkPos.z)) { - // the post processing is expensive, so we don't want to run it unless we're actually near - // a player. -- chunkProviderServer.mainThreadProcessor.execute(() -> { -+ chunkProviderServer.mainThreadProcessor.execute(this.chunkPos.x, this.chunkPos.z, () -> { // Gale - base thread pool - chunk-sorted cache tasks - if (!LevelChunk.this.areNeighboursLoaded(1)) { - return; - } -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 1c2220ff497db66a48ae47cff9103c424ff37bc8..1cdcf0b7ec11c7bda92aeed2f3470e9419e1214e 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -371,7 +371,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { - if (!Bukkit.isPrimaryThread()) { - return java.util.concurrent.CompletableFuture.supplyAsync(() -> { - return CraftWorld.this.isChunkGenerated(x, z); -- }, world.getChunkSource().mainThreadProcessor).join(); -+ }, world.getChunkSource().mainThreadProcessor.createExecutorForChunk(x, z)).join(); // Gale - base thread pool - chunk-sorted cache tasks - } - ChunkAccess chunk = world.getChunkSource().getChunkAtImmediately(x, z); - if (chunk == null) { -@@ -479,7 +479,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { - list, - true - ); -- serverChunkCache.mainThreadProcessor.managedBlock(future::isDone); -+ serverChunkCache.mainThreadProcessor.managedYield(future); // Gale - base thread pool - if (chunkStatus == ChunkStatus.NOISE) { - future.join().left().ifPresent(chunk -> net.minecraft.world.level.levelgen.Heightmap.primeHeightmaps(chunk, ChunkStatus.POST_FEATURES)); - } -diff --git a/src/main/java/org/galemc/gale/executor/ClosestChunkBlockableEventLoop.java b/src/main/java/org/galemc/gale/executor/ClosestChunkBlockableEventLoop.java -new file mode 100644 -index 0000000000000000000000000000000000000000..5048444f22b2718d5f4b0429fcb65655e0c5a3c0 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/ClosestChunkBlockableEventLoop.java -@@ -0,0 +1,590 @@ -+// Gale - base thread pool - chunk-sorted cache tasks -+ -+package org.galemc.gale.executor; -+ -+import com.destroystokyo.paper.util.misc.DistanceTrackingAreaMap; -+import com.destroystokyo.paper.util.misc.PlayerDistanceTrackingAreaMap; -+import io.papermc.paper.util.IntegerUtil; -+import io.papermc.paper.util.MCUtil; -+import it.unimi.dsi.fastutil.longs.*; -+import net.minecraft.util.thread.BlockableEventLoop; -+import org.galemc.gale.concurrent.Mutex; -+import org.galemc.gale.executor.annotation.Access; -+import org.galemc.gale.executor.annotation.Guarded; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.thread.ServerThread; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.*; -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.Executor; -+import java.util.function.Supplier; -+ -+/** -+ * A {@link BlockableEventLoop} for which all tasks relate to a chunk, where the chunks with the smallest -+ * object distance in a given {@link DistanceTrackingAreaMap} have the highest priority (i.e. are executed first). -+ * have the highest priority. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public abstract class ClosestChunkBlockableEventLoop extends BlockableEventLoop { -+ -+ /** -+ * @return A packing of the chunk key, similar to {@link MCUtil#getCoordinateKey}, but -+ * instead of allocating 32 bits for each coordinate, only 27 bits are used. -+ * Of those 27 bits, 1 bit is used for the sign (1 indicating negative, 0 indicating nonnegative), -+ * and 26 bits for the absolute value, allowing a signed value in the -+ * range [-67108863, 67108863] per coordinate (there are two ways to represent 0, of which only the one -+ * with a nonnegative sign bit (0) is used). -+ * With 2 coordinates, this leaves 10 unused bits at the most significant end, -+ * allowing an extra unsigned 9-bit value in the range [0, 511] to be stored, -+ * and leaving the final (most significant) bit always zero. -+ */ -+ private static long getTightlyPackedXZ(final int x, final int z) { -+ return ((z & 0x80000000L) << 22) | ((IntegerUtil.branchlessAbs(z) & 0x03FFFFFFL) << 27) | ((x & 0x80000000L) >> 5) | (IntegerUtil.branchlessAbs(x) & 0x03FFFFFFL); -+ } -+ -+ /** -+ * A packed long containing chunk coordinates and a distance. Because the distance is stored in the most -+ * significant bits (and the sign bit of the long is unused), sorting these values in ascending order -+ * always sorts them primarily by their distance in ascending order (and secondarily by their chunk coordinates, -+ * in an order that is irrelevant apart from being deterministic). -+ * -+ * @param tightlyPackedXZ The return value pf {@link #getTightlyPackedXZ(int, int)}. -+ * @param distance A distance in the range [0, 511]. -+ * @return The same as {@link #getTightlyPackedXZ(int, int)}, but with the additional distance value -+ * stored in the 9 bits at indices [1..9] (where index 0 indicates the most significant bit) -+ * as an unsigned integer. -+ * -+ * @see #getTightlyPackedXZ(int, int) -+ */ -+ private static long getTightlyPackedXZWithDistance(final long tightlyPackedXZ, final int distance) { -+ return tightlyPackedXZ | ((distance & 0x000001FFL) << 54); -+ } -+ -+ private static long stripTightlyPackedDistance(final long tightlyPackedXZWithDistance) { -+ return tightlyPackedXZWithDistance & 0x003FFFFFFFFFFFFFL; -+ } -+ -+ private static int unpackTightlyPackedX(final long tightlyPackedXZ) { -+ long sign = (tightlyPackedXZ >> 26) & 1L; -+ return (int) (((-sign) ^ (tightlyPackedXZ & 0x03FFFFFFL)) + sign); -+ } -+ -+ private static int unpackTightlyPackedZ(final long tightlyPackedXZ) { -+ long sign = (tightlyPackedXZ >> 53) & 1L; -+ return (int) (((-sign) ^ ((tightlyPackedXZ >> 27) & 0x03FFFFFFL)) + sign); -+ } -+ -+ private static int unpackTightlyPackedDistance(final long tightlyPackedXZWithDistance) { -+ return (int) (tightlyPackedXZWithDistance >> 54); -+ } -+ -+ /** -+ * The {@link DistanceTrackingAreaMap} to get distances from. -+ * This must be set after construction with {@link #setAreaMap}. -+ */ -+ private PlayerDistanceTrackingAreaMap areaMap; -+ -+ private final Mutex lock = Mutex.create(); -+ -+ /** -+ * A pool of re-usable task queues. -+ *
    -+ * This pool is used as a LIFO stack. -+ */ -+ @Guarded("#lock") -+ private final ArrayList> taskQueuePool = new ArrayList<>(); -+ -+ /** -+ * The last known distance for a chunk, by their {@linkplain #getTightlyPackedXZ(int, int) chunk key}. -+ *
    -+ * Only contains values for chunks that have tasks in {@link #tasksPerChunk}. -+ * Does not contain a value for the {@link //#prepolledRunnable}. -+ * For other tasks, the default return value of {@link Long2IntMap#get} is -1. -+ */ -+ @Guarded("#lock") -+ private final Long2IntMap distancePerChunk = new Long2IntOpenHashMap(); -+ { -+ distancePerChunk.defaultReturnValue(-1); -+ } -+ -+ /** -+ * The tasks stored for a specific {@linkplain #getTightlyPackedXZ(int, int) chunk key}. -+ * Each chunk has its tasks stored as a FIFO queue. -+ */ -+ @Guarded("#lock") -+ private final Long2ObjectMap> tasksPerChunk = new Long2ObjectOpenHashMap<>(); -+ -+ /** -+ * The chunks with tasks, stored as a -+ * {@linkplain #getTightlyPackedXZWithDistance(long, int) packed chunk key and distance} -+ * (where the distance is {@linkplain DistanceTrackingAreaMap#getNearestObjectDistance measured} -+ * at the time of adding). -+ */ -+ @Guarded("#lock") -+ private final LongAVLTreeSet chunkQueue = new LongAVLTreeSet(); -+ -+ @Guarded(value = "#lock", fieldAccess = Access.WRITE) -+ private volatile int pendingTaskCount = 0; -+ -+ /** -+ * An ordered list of chunks to update to a new distance, where each triple of elements in the array -+ * consists of the chunk x, chunk z, and new distance. -+ */ -+ @Guarded(value = "#lock") -+ private int[] chunkDistanceUpdates = new int[0]; -+ -+ /** -+ * The number of chunk distance updates in {@link #chunkDistanceUpdates}. -+ */ -+ @Guarded(value = "#lock") -+ private int chunkDistanceUpdateLength; -+ -+ /** -+ * A flag indicating the server thread wants to add new chunk distance updates, -+ * but is waiting for the {@link #lock}. -+ */ -+ private volatile boolean serverThreadWantsLockToAddChunkDistanceUpdates; -+ -+// /** -+// * A pre-polled task to increase the speed of {@link #pollTask()} calls made by the server thread. -+// */ -+// @Guarded("#lock") -+// private @Nullable R prepolledRunnable; -+ -+// /** -+// * The value of {@link #getTightlyPackedXZ(int, int)} for the {@link #prepolledRunnable}, if it is not null. -+// * Otherwise, an arbitrary value. -+// */ -+// @Guarded("#lock") -+// private @Nullable long prepolledRunnablePackedXZ; -+ -+// /** -+// * The last known distance for the {@link #prepolledRunnable}, if it is not null. -+// * Otherwise, an arbitrary value. -+// */ -+// @Guarded("#lock") -+// private @Nullable int prepolledRunnableDistance; -+ -+ public ClosestChunkBlockableEventLoop(String name) { -+ super(name); -+ } -+ -+ public void setAreaMap(PlayerDistanceTrackingAreaMap areaMap) { -+ if (this.areaMap != null) { -+ throw new IllegalStateException("Called " + this.getClass().getName() + ".setAreaMap(areaMap) but it was already called before"); -+ } -+ this.areaMap = areaMap; -+ } -+ -+ /** -+ * Provisions a task queue, either a recycled queue or a newly created one. -+ *
    -+ * This method must only be called while {@link #lock} is held. -+ */ -+ private Queue provisionTaskQueue() { -+ return this.taskQueuePool.isEmpty() ? new ArrayDeque<>(1) : this.taskQueuePool.remove(this.taskQueuePool.size() - 1); -+ } -+ -+ /** -+ * Returns a task queue to the pool. -+ *
    -+ * This method must only be called while {@link #lock} is held. -+ * -+ * @param queue An already empty task queue. -+ */ -+ private void recycleTaskQueue(Queue queue) { -+ this.taskQueuePool.add(queue); -+ } -+ -+ /** -+ * Processes all chunk update tasks in {@link #chunkDistanceUpdates}. -+ *
    -+ * This method must only be called while {@link #lock} is held. -+ */ -+ private void processChunkDistanceUpdates() { -+// boolean madeChangesSinceLastPrepoll = false; -+ boolean isNonServerThread = !(Thread.currentThread() instanceof ServerThread); -+ while (this.chunkDistanceUpdateLength > 0) { -+ -+ // Let the server thread add new chunk distance updates -+ if (isNonServerThread && this.serverThreadWantsLockToAddChunkDistanceUpdates) { -+// if (madeChangesSinceLastPrepoll) { -+// this.preparePrepolledRunnable(); -+// } -+ this.lock.release(); -+ while (this.serverThreadWantsLockToAddChunkDistanceUpdates) { -+ Thread.onSpinWait(); -+ } -+ this.lock.spinLock(); -+ } -+ -+ // Read the change -+ int chunkX = this.chunkDistanceUpdates[this.chunkDistanceUpdateLength * 3 - 3]; -+ int chunkZ = this.chunkDistanceUpdates[this.chunkDistanceUpdateLength * 3 - 2]; -+ int newDistance = this.chunkDistanceUpdates[this.chunkDistanceUpdateLength * 3 - 1]; -+ this.chunkDistanceUpdateLength--; -+ -+ // Apply the change -+ long packedXZ = getTightlyPackedXZ(chunkX, chunkZ); -+ // Apply the change to the pre-polled task -+// if (this.prepolledRunnablePackedXZ == packedXZ && this.prepolledRunnable != null) { -+// this.prepolledRunnableDistance = newDistance; -+// } -+ // If we don't have tasks for this queue, skip applying the change to the queue -+ int oldDistance = this.distancePerChunk.get(packedXZ); -+ if (oldDistance == -1) { -+ return; -+ } -+ long oldPackedXZWithDistance = getTightlyPackedXZWithDistance(packedXZ, oldDistance); -+ long newPackedXZWithDistance = getTightlyPackedXZWithDistance(packedXZ, newDistance); -+ this.distancePerChunk.put(packedXZ, newDistance); -+ this.chunkQueue.remove(oldPackedXZWithDistance); -+ this.chunkQueue.add(newPackedXZWithDistance); -+ -+// madeChangesSinceLastPrepoll = true; -+ -+ } -+// if (madeChangesSinceLastPrepoll) { -+// this.preparePrepolledRunnable(); -+// } -+ } -+ -+ public void onChunkDistanceChange(int chunkX, int chunkZ, int newDistance) { -+ /* -+ Make sure the distance is in the range [0, 511]. -+ A negative or very high value may indicate the chunk is not within the tracking range of the AreaMap, -+ so those values are replaced by the maximum value that is in range. -+ */ -+ int newDistanceWithinRange = newDistance < 0 || newDistance >= 512 ? 511 : newDistance; -+ long packedXZ = getTightlyPackedXZ(chunkX, chunkZ); -+ boolean setWantsToLock = false; -+ while (!this.lock.tryAcquire()) { -+ if (!setWantsToLock) { -+ setWantsToLock = true; -+ this.serverThreadWantsLockToAddChunkDistanceUpdates = true; -+ } -+ Thread.onSpinWait(); -+ } -+ try { -+ if (setWantsToLock) { -+ this.serverThreadWantsLockToAddChunkDistanceUpdates = false; -+ } -+ -+ // If we don't have tasks for this queue, we don't need the change -+ int oldDistance = this.distancePerChunk.get(packedXZ); -+ if (oldDistance == -1) { -+ return; -+ } -+ -+ // Schedule applying the change -+ this.chunkDistanceUpdateLength++; -+ if (this.chunkDistanceUpdates.length < this.chunkDistanceUpdateLength * 3) { -+ this.chunkDistanceUpdates = Arrays.copyOf(this.chunkDistanceUpdates, this.chunkDistanceUpdateLength * 6); -+ } -+ this.chunkDistanceUpdates[this.chunkDistanceUpdateLength * 3 - 3] = chunkX; -+ this.chunkDistanceUpdates[this.chunkDistanceUpdateLength * 3 - 2] = chunkZ; -+ this.chunkDistanceUpdates[this.chunkDistanceUpdateLength * 3 - 1] = newDistanceWithinRange; -+ -+ } finally { -+ this.lock.release(); -+ } -+ } -+ -+// /** -+// * Sets {@link #prepolledRunnable} appropriately, -+// * potentially returning the previous value to the {@link #tasksPerChunk}. -+// *
    -+// * This method must only be called while {@link #lock} is held. -+// */ -+// private void preparePrepolledRunnable() { -+// if (this.prepolledRunnable == null) { -+// this.pollFromQueueIntoPrepolled(); -+// return; -+// } -+// if (this.chunkQueue.isEmpty()) { -+// return; -+// } -+// long firstPackedXZWithDistanceInQueue = this.chunkQueue.firstLong(); -+// int firstDistanceInQueue = unpackTightlyPackedDistance(firstPackedXZWithDistanceInQueue); -+// // Swap the pre-polled task if necessary -+// if (firstDistanceInQueue < this.prepolledRunnableDistance) { -+// // Return the pre-polled task to the queue -+// long packedXZWithDistance = getTightlyPackedXZWithDistance(this.prepolledRunnablePackedXZ, this.prepolledRunnableDistance); -+// this.tasksPerChunk.computeIfAbsent(this.prepolledRunnablePackedXZ, $ -> this.provisionTaskQueue()).add(this.prepolledRunnable); -+// this.chunkQueue.add(packedXZWithDistance); -+// this.distancePerChunk.putIfAbsent(this.prepolledRunnablePackedXZ, this.prepolledRunnableDistance); -+// this.prepolledRunnable = null; -+// // Set a new pre-polled task -+// this.pollFromQueueIntoPrepolled(); -+// } -+// } -+ -+ public final Executor createExecutorForChunk(int chunkX, int chunkZ) { -+ return command -> this.execute(chunkX, chunkZ, command); -+ } -+ -+ @Override -+ public int getPendingTasksCount() { -+ return this.pendingTaskCount; -+ } -+ -+ @Override -+ public boolean hasPendingTasks() { -+ return this.pendingTaskCount > 0; -+ } -+ -+ @Override -+ public @NotNull CompletableFuture submit(@NotNull Supplier task) { -+ throw new UnsupportedOperationException("Called " + this.getClass().getName() + ".submit(Supplier), use submit(int, int, Supplier) instead"); -+ } -+ -+ /** -+ * @see #submit(Supplier) -+ */ -+ public CompletableFuture submit(int chunkX, int chunkZ, Supplier task) { -+ return this.scheduleExecutables() ? CompletableFuture.supplyAsync(task, this.createExecutorForChunk(chunkX, chunkZ)) : CompletableFuture.completedFuture(task.get()); -+ } -+ -+ @Override -+ protected @NotNull CompletableFuture submitAsync(@NotNull Runnable runnable) { -+ throw new UnsupportedOperationException("Called " + this.getClass().getName() + ".submitAsync(Runnable), use submitAsync(int, int, Runnable) instead"); -+ } -+ -+ /** -+ * @see #submitAsync(Runnable) -+ */ -+ private CompletableFuture submitAsync(int chunkX, int chunkZ, Runnable runnable) { -+ return CompletableFuture.supplyAsync(() -> { -+ runnable.run(); -+ return null; -+ }, this.createExecutorForChunk(chunkX, chunkZ)); -+ } -+ -+ @Override -+ public @NotNull CompletableFuture submit(@NotNull Runnable task) { -+ throw new UnsupportedOperationException("Called " + this.getClass().getName() + ".submit(Runnable), use submit(int, int, Runnable) instead"); -+ } -+ -+ /** -+ * @see #submit(Runnable) -+ */ -+ public CompletableFuture submit(int chunkX, int chunkZ, Runnable task) { -+ if (this.scheduleExecutables()) { -+ return this.submitAsync(chunkX, chunkZ, task); -+ } else { -+ task.run(); -+ return CompletableFuture.completedFuture((Void)null); -+ } -+ } -+ -+ public void executeBlocking(@NotNull Runnable runnable) { -+ throw new UnsupportedOperationException("Called " + this.getClass().getName() + ".executeBlocking(Runnable), use executeBlocking(int, int, Runnable) instead"); -+ } -+ -+ /** -+ * @see #executeBlocking(Runnable) -+ */ -+ public void executeBlocking(int chunkX, int chunkZ, Runnable runnable) { -+ if (!this.isSameThread()) { -+ this.submitAsync(chunkX, chunkZ, runnable).join(); -+ } else { -+ runnable.run(); -+ } -+ } -+ -+ @Override -+ public void scheduleOnMain(@NotNull Runnable r0) { -+ throw new UnsupportedOperationException("Called " + this.getClass().getName() + ".scheduleOnMain(Runnable), use scheduleOnMain(int, int, Runnable) instead"); -+ } -+ -+ /** -+ * @see #scheduleOnMain(Runnable) -+ */ -+ public void scheduleOnMain(int chunkX, int chunkZ, Runnable r0) { -+ this.tell(chunkX, chunkZ, this.wrapRunnable(r0)); -+ } -+ -+ @Override -+ public void tell(@NotNull R runnable) { -+ throw new UnsupportedOperationException("Called " + this.getClass().getName() + ".tell(R), use tell(int, int, R) instead"); -+ } -+ -+ /** -+ * Schedules a task related to the chunk with the given chunk coordinates. -+ * -+ * @see #tell(R) -+ */ -+ public void tell(int chunkX, int chunkZ, R runnable) { -+ long packedXZ = getTightlyPackedXZ(chunkX, chunkZ); -+ int computedDistance = this.areaMap.getNearestObjectDistance(chunkX, chunkZ); -+ int computedDistanceInRange = computedDistance < 0 || computedDistance >= 512 ? 511 : computedDistance; -+ try (var ignored = this.lock.withSpinLock()) { -+// if (this.prepolledRunnable == null && this.chunkQueue.isEmpty()) { -+// // Set the pre-polled runnable right away -+// this.prepolledRunnable = runnable; -+// this.prepolledRunnablePackedXZ = packedXZ; -+// this.prepolledRunnableDistance = computedDistanceInRange; -+// } else { -+ this.processChunkDistanceUpdates(); -+ int distance = this.distancePerChunk.get(packedXZ); -+// if (distance == -1 && packedXZ == this.prepolledRunnablePackedXZ && this.prepolledRunnable != null) { -+// // Use the value from the pre-polled task -+// distance = this.prepolledRunnableDistance; -+// // Keep it consistent with the queue -+// this.distancePerChunk.put(packedXZ, computedDistanceInRange); -+// } -+ if (distance == -1) { -+ // Set a known distance -+ distance = computedDistanceInRange; -+ this.distancePerChunk.put(packedXZ, computedDistanceInRange); -+ // Keep it consistent with the pre-polled task -+// if (this.prepolledRunnablePackedXZ == packedXZ) { -+// this.prepolledRunnableDistance = computedDistanceInRange; -+// } -+ } -+ long packedXZWithDistance = getTightlyPackedXZWithDistance(packedXZ, distance); -+ this.tasksPerChunk.computeIfAbsent(packedXZ, $ -> this.provisionTaskQueue()).add(runnable); -+ this.chunkQueue.add(packedXZWithDistance); -+// this.preparePrepolledRunnable(); -+// } -+ //noinspection NonAtomicOperationOnVolatileField -+ this.pendingTaskCount++; -+ } -+ } -+ -+ @Override -+ public void execute(@NotNull Runnable runnable) { -+ throw new UnsupportedOperationException("Called " + this.getClass().getName() + ".execute(Runnable), use execute(int, int, Runnable) instead"); -+ } -+ -+ /** -+ * @see #execute(Runnable) -+ */ -+ public void execute(int chunkX, int chunkZ, Runnable runnable) { -+ if (this.scheduleExecutables()) { -+ this.tell(chunkX, chunkZ, this.wrapRunnable(runnable)); -+ } else { -+ runnable.run(); -+ } -+ } -+ -+ @Override -+ public void executeIfPossible(@NotNull Runnable runnable) { -+ throw new UnsupportedOperationException("Called " + this.getClass().getName() + ".executeIfPossible(Runnable), use executeIfPossible(int, int, Runnable) instead"); -+ } -+ -+ /** -+ * @see #executeIfPossible(Runnable) -+ */ -+ public void executeIfPossible(int chunkX, int chunkZ, Runnable runnable) { -+ this.execute(chunkX, chunkZ, runnable); -+ } -+ -+ @Override -+ protected void dropAllTasks() { -+ try (var ignored = this.lock.withSpinLock()) { -+ this.chunkDistanceUpdateLength = 0; -+ this.distancePerChunk.clear(); -+ this.tasksPerChunk.forEach(($, queue) -> this.recycleTaskQueue(queue)); -+ this.tasksPerChunk.clear(); -+ this.pendingTaskCount = 0; -+ this.chunkQueue.clear(); -+// this.prepolledRunnable = null; -+ } -+ } -+ -+// /** -+// * Polls from the {@link #tasksPerChunk}, without checking {@link #prepolledRunnable}, -+// * and stores the result in the {@link #prepolledRunnable}. If no task is polled, {@link #prepolledRunnable} -+// * is not modified (particularly, it is not cleared), -+// * so this task must only be called while {@link #prepolledRunnable} is null. -+// *
    -+// * This method will not make a call to {@link #processChunkDistanceUpdates()}: if necessary, such a call -+// * must be made beforehand. -+// *
    -+// * This method must only be called while {@link #lock} is held. -+// */ -+// private void pollFromQueueIntoPrepolled() { -+// long packedXZWithDistance; -+// long packedXZ; -+// Queue tasks; -+// do { -+// if (this.chunkQueue.isEmpty()) { -+// return; -+// } -+// packedXZWithDistance = this.chunkQueue.firstLong(); -+// packedXZ = stripTightlyPackedDistance(packedXZWithDistance); -+// tasks = this.tasksPerChunk.get(packedXZ); -+// // Hot-fix for when tasks is null, but this *should* not be happening (but it currently happens sometimes) -+// } while (tasks == null || tasks.isEmpty()); -+// this.prepolledRunnable = tasks.poll(); -+// this.prepolledRunnablePackedXZ = packedXZ; -+// this.prepolledRunnableDistance = unpackTightlyPackedDistance(packedXZWithDistance); -+// if (tasks.isEmpty()) { -+// this.distancePerChunk.remove(packedXZ); -+// this.recycleTaskQueue(tasks); -+// this.tasksPerChunk.remove(packedXZ); -+// this.chunkQueue.remove(packedXZWithDistance); -+// } -+// } -+ -+ private @Nullable R pollFromQueue() { -+ long packedXZWithDistance; -+ long packedXZ; -+ Queue tasks; -+ if (this.chunkQueue.isEmpty()) { -+ return null; -+ } -+ packedXZWithDistance = this.chunkQueue.firstLong(); -+ packedXZ = stripTightlyPackedDistance(packedXZWithDistance); -+ tasks = this.tasksPerChunk.get(packedXZ); -+ R task = tasks.peek(); -+ if (this.blockingCount == 0 && !this.shouldRun(task)) { -+ return null; -+ } -+ tasks.poll(); -+ if (tasks.isEmpty()) { -+ this.distancePerChunk.remove(packedXZ); -+ this.recycleTaskQueue(tasks); -+ this.tasksPerChunk.remove(packedXZ); -+ this.chunkQueue.remove(packedXZWithDistance); -+ } -+ return task; -+ } -+ -+ @Override -+ public boolean pollTask() { -+ if (this.pendingTaskCount == 0) { -+ return false; -+ } -+ R runnable; -+ try (var ignored = this.lock.withSpinLock()) { -+ this.processChunkDistanceUpdates(); -+ // Pre-poll a task if necessary -+// if (this.prepolledRunnable == null) { -+// this.pollFromQueueIntoPrepolled(); -+// } -+// runnable = this.prepolledRunnable; -+ runnable = this.pollFromQueue(); -+ // If it is still null, there are no tasks -+ if (runnable == null) { -+ return false; -+ } -+// if (this.blockingCount == 0 && !this.shouldRun(runnable)) { -+// return false; -+// } -+// this.prepolledRunnable = null; -+ //noinspection NonAtomicOperationOnVolatileField -+ this.pendingTaskCount--; -+ } -+ this.doRunTask(runnable); -+ return true; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledChunkCacheTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledChunkCacheTaskQueue.java -new file mode 100644 -index 0000000000000000000000000000000000000000..fe2e06a827555d81a30697f8b08667692a3eeade ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledChunkCacheTaskQueue.java -@@ -0,0 +1,52 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.queue; -+ -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.level.ServerChunkCache; -+import net.minecraft.server.level.ServerLevel; -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.thread.ServerThread; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * This class provides access to, but does not store, the tasks scheduled to be executed on the main thread, -+ * that are scheduled and normally polled by each world's {@link ServerChunkCache#mainThreadProcessor} in their -+ * respective {@link ServerChunkCache.MainThreadExecutor#managedBlock}. These tasks could normally also be run in the -+ * server's {@link MinecraftServer#managedBlock} if there were no more global scheduled server thread tasks, and as -+ * such we provide access to polling these tasks from a {@link ServerThread}. -+ *
    -+ * All tasks provided by this queue must be yield-free. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public final class AllLevelsScheduledChunkCacheTaskQueue extends AllLevelsScheduledTaskQueue { -+ -+ AllLevelsScheduledChunkCacheTaskQueue() { -+ super(TaskSpan.YIELDING, false); -+ } -+ -+ @Override -+ public String getName() { -+ return "AllLevelsScheduledChunkCache"; -+ } -+ -+ @Override -+ protected boolean hasLevelTasks(ServerLevel level) { -+ return level.getChunkSource().mainThreadProcessor.hasPendingTasks(); -+ } -+ -+ @Override -+ protected @Nullable Runnable pollLevel(ServerLevel level) { -+ var executor = level.getChunkSource().mainThreadProcessor; -+ if (executor.hasPendingTasks()) { -+ return executor::pollTask; -+ } -+ return null; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java -index b4172f285fbed1f314891b2f729aa2dc27b9ab9b..ed642b13e95479d0ec98731a3f5b74cf2fb78f81 100644 ---- a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java -+++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java -@@ -12,7 +12,8 @@ import org.galemc.gale.executor.thread.pool.BaseThreadActivation; - import org.jetbrains.annotations.Nullable; - - /** -- * Common implementation for queues with scheduled tasks for all levels. -+ * Common implementation for queues with scheduled tasks for all levels, -+ * such as {@link AllLevelsScheduledChunkCacheTaskQueue}. - *
    - * All tasks provided by this queue must be yield-free. - * -diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -index f4adcdcad96b2748c60aecb8f5c25370ee6e8f5b..8465ce8de44d823aac4784fbc5183b9fc49b2825 100644 ---- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -@@ -61,7 +61,8 @@ public enum BaseTaskQueueTier { - SERVER(new AbstractTaskQueue[]{ - BaseTaskQueues.deferredToServerThread, - BaseTaskQueues.serverThreadTick, -- BaseTaskQueues.anyTickScheduledServerThread -+ BaseTaskQueues.anyTickScheduledServerThread, -+ BaseTaskQueues.allLevelsScheduledChunkCache - }, MinecraftServer.SERVER_THREAD_PRIORITY), - /** - * A tier for queues that contain tasks that are part of ticking, -diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -index 92721a51268becb05d708db04e9d6daaa66fb8b2..c608cdfc17e02a37e8f1799af2b26f973a32c839 100644 ---- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -@@ -90,6 +90,11 @@ public final class BaseTaskQueues { - */ - public static final SimpleTaskQueue tickAssist = SimpleTaskQueue.allSpans("TickAssist"); - -+ /** -+ * @see AllLevelsScheduledChunkCacheTaskQueue -+ */ -+ public static final AllLevelsScheduledChunkCacheTaskQueue allLevelsScheduledChunkCache = new AllLevelsScheduledChunkCacheTaskQueue(); -+ - /** - * This queue stores the tasks posted to {@link MCUtil#cleanerExecutor}. - */ diff --git a/patches/server/0163-Run-TickThread-chunk-tasks-on-base-thread-pool.patch b/patches/server/0163-Run-TickThread-chunk-tasks-on-base-thread-pool.patch deleted file mode 100644 index a9b72aa..0000000 --- a/patches/server/0163-Run-TickThread-chunk-tasks-on-base-thread-pool.patch +++ /dev/null @@ -1,187 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Sun, 29 Jan 2023 22:43:55 +0100 -Subject: [PATCH] Run TickThread chunk tasks on base thread pool - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadedTaskQueue.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadedTaskQueue.java -index b71404be2c82f7db35272b367af861e94d6c73d3..0b4ae235398eda804d6facd4db74c721e9d76b57 100644 ---- a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadedTaskQueue.java -+++ b/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadedTaskQueue.java -@@ -1,10 +1,15 @@ - package ca.spottedleaf.concurrentutil.executor.standard; - -+import net.minecraft.server.MinecraftServer; -+import org.galemc.gale.executor.queue.BaseTaskQueues; -+ - import java.util.ArrayDeque; - import java.util.concurrent.atomic.AtomicLong; - - public class PrioritisedThreadedTaskQueue implements PrioritisedExecutor { - -+ private final boolean influenceMayHaveDelayedTasks; // Gale - base thread pool -+ - protected final ArrayDeque[] queues = new ArrayDeque[Priority.TOTAL_SCHEDULABLE_PRIORITIES]; { - for (int i = 0; i < Priority.TOTAL_SCHEDULABLE_PRIORITIES; ++i) { - this.queues[i] = new ArrayDeque<>(); -@@ -20,6 +25,16 @@ public class PrioritisedThreadedTaskQueue implements PrioritisedExecutor { - - protected long taskIdGenerator = 0; - -+ public PrioritisedThreadedTaskQueue() { -+ this(false); -+ } -+ -+ // Gale start - base thread pool -+ public PrioritisedThreadedTaskQueue(boolean influenceMayHaveDelayedTasks) { -+ this.influenceMayHaveDelayedTasks = influenceMayHaveDelayedTasks; -+ } -+ // Gale end - base thread pool -+ - @Override - public PrioritisedExecutor.PrioritisedTask queueRunnable(final Runnable task, final PrioritisedExecutor.Priority priority) throws IllegalStateException, IllegalArgumentException { - if (!PrioritisedExecutor.Priority.isValidPriority(priority)) { -@@ -145,6 +160,12 @@ public class PrioritisedThreadedTaskQueue implements PrioritisedExecutor { - } - - protected final long getAndAddTotalScheduledTasksVolatile(final long value) { -+ // Gale start - base thread pool -+ if (this.influenceMayHaveDelayedTasks) { -+ MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks = true; -+ BaseTaskQueues.allLevelsScheduledTickThreadChunk.newTaskWasAdded(); -+ } -+ // Gale end - base thread pool - return this.totalScheduledTasks.getAndAdd(value); - } - -@@ -158,6 +179,12 @@ public class PrioritisedThreadedTaskQueue implements PrioritisedExecutor { - return this.totalCompletedTasks.getAndAdd(value); - } - -+ // Gale start - base thread pool -+ public final boolean hasScheduledUncompletedTasksVolatile() { -+ return this.totalScheduledTasks.get() > this.totalCompletedTasks.get(); -+ } -+ // Gale end - base thread pool -+ - protected static final class PrioritisedTask implements PrioritisedExecutor.PrioritisedTask { - protected final PrioritisedThreadedTaskQueue queue; - protected long id; -diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java -index 84cc9397237fa0c17aa1012dfb5683c90eb6d3b8..f5c15d40094c2ddc6220b0595597d12103fcf425 100644 ---- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java -+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java -@@ -113,7 +113,7 @@ public final class ChunkTaskScheduler { - public final PrioritisedThreadPool.PrioritisedPoolExecutor parallelGenExecutor; - public final PrioritisedThreadPool.PrioritisedPoolExecutor loadExecutor; - -- private final PrioritisedThreadedTaskQueue mainThreadExecutor = new PrioritisedThreadedTaskQueue(); -+ public final PrioritisedThreadedTaskQueue mainThreadExecutor = new PrioritisedThreadedTaskQueue(true); // Gale - base thread pool - private -> public, count delayed tasks - - final ReentrantLock schedulingLock = new ReentrantLock(); - public final ChunkHolderManager chunkHolderManager; -diff --git a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java -index ed642b13e95479d0ec98731a3f5b74cf2fb78f81..05b1817756925c49b1228ff79a5e11b37bc75834 100644 ---- a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java -+++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java -@@ -13,7 +13,7 @@ import org.jetbrains.annotations.Nullable; - - /** - * Common implementation for queues with scheduled tasks for all levels, -- * such as {@link AllLevelsScheduledChunkCacheTaskQueue}. -+ * such as {@link AllLevelsScheduledChunkCacheTaskQueue} and {@link AllLevelsScheduledTickThreadChunkTaskQueue}. - *
    - * All tasks provided by this queue must be yield-free. - * -diff --git a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTickThreadChunkTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTickThreadChunkTaskQueue.java -new file mode 100644 -index 0000000000000000000000000000000000000000..0e893e7fca1ae3831c3de3a8966e086616e1003c ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTickThreadChunkTaskQueue.java -@@ -0,0 +1,54 @@ -+// Gale - base thread pool -+ -+package org.galemc.gale.executor.queue; -+ -+import io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler; -+import io.papermc.paper.util.TickThread; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.level.ServerChunkCache; -+import net.minecraft.server.level.ServerLevel; -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * This class provides access to, but does not store, the tasks scheduled to be executed on the main thread, -+ * that are scheduled and normally polled by each world's {@link ServerLevel#chunkTaskScheduler} using -+ * respective {@link ChunkTaskScheduler#executeMainThreadTask}. These tasks could normally also be run in the -+ * server's {@link MinecraftServer#managedBlock} or a level's {@link ServerChunkCache}'s -+ * {@link ServerChunkCache.MainThreadExecutor#managedBlock}, and as such we provide access to polling these tasks -+ * from a {@link TickThread}. -+ *
    -+ * All tasks provided by this queue must be yield-free. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public final class AllLevelsScheduledTickThreadChunkTaskQueue extends AllLevelsScheduledTaskQueue { -+ -+ AllLevelsScheduledTickThreadChunkTaskQueue() { -+ super(TaskSpan.YIELDING, true); -+ } -+ -+ @Override -+ public String getName() { -+ return "AllLevelsScheduledTickThreadChunk"; -+ } -+ -+ @Override -+ protected boolean hasLevelTasks(ServerLevel level) { -+ return level.chunkTaskScheduler.mainThreadExecutor.hasScheduledUncompletedTasksVolatile(); -+ } -+ -+ @Override -+ protected @Nullable Runnable pollLevel(ServerLevel level) { -+ var executor = level.chunkTaskScheduler.mainThreadExecutor; -+ if (executor.hasScheduledUncompletedTasksVolatile()) { -+ return executor::executeTask; -+ } -+ return null; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -index 8465ce8de44d823aac4784fbc5183b9fc49b2825..0035d638667c6e0707ecf3e3c040f0123f8e68d5 100644 ---- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -@@ -62,7 +62,8 @@ public enum BaseTaskQueueTier { - BaseTaskQueues.deferredToServerThread, - BaseTaskQueues.serverThreadTick, - BaseTaskQueues.anyTickScheduledServerThread, -- BaseTaskQueues.allLevelsScheduledChunkCache -+ BaseTaskQueues.allLevelsScheduledChunkCache, -+ BaseTaskQueues.allLevelsScheduledTickThreadChunk - }, MinecraftServer.SERVER_THREAD_PRIORITY), - /** - * A tier for queues that contain tasks that are part of ticking, -diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -index c608cdfc17e02a37e8f1799af2b26f973a32c839..ed3ccf2e64539363a7be2d507c68c40b5913f75c 100644 ---- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -@@ -95,6 +95,11 @@ public final class BaseTaskQueues { - */ - public static final AllLevelsScheduledChunkCacheTaskQueue allLevelsScheduledChunkCache = new AllLevelsScheduledChunkCacheTaskQueue(); - -+ /** -+ * @see AllLevelsScheduledTickThreadChunkTaskQueue -+ */ -+ public static final AllLevelsScheduledTickThreadChunkTaskQueue allLevelsScheduledTickThreadChunk = new AllLevelsScheduledTickThreadChunkTaskQueue(); -+ - /** - * This queue stores the tasks posted to {@link MCUtil#cleanerExecutor}. - */ diff --git a/patches/server/0164-BaseThread-PrioritisedQueueExecutorThread-agent-util.patch b/patches/server/0164-BaseThread-PrioritisedQueueExecutorThread-agent-util.patch deleted file mode 100644 index db13b20..0000000 --- a/patches/server/0164-BaseThread-PrioritisedQueueExecutorThread-agent-util.patch +++ /dev/null @@ -1,337 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Mon, 30 Jan 2023 22:34:48 +0100 -Subject: [PATCH] BaseThread PrioritisedQueueExecutorThread agent utility - -License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedQueueExecutorThread.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedQueueExecutorThread.java -index 91fe0f7049122f62f05ba09c24cba5d758340cff..a26735a795052d6770498561826e8c5c1bf9fa79 100644 ---- a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedQueueExecutorThread.java -+++ b/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedQueueExecutorThread.java -@@ -2,6 +2,7 @@ package ca.spottedleaf.concurrentutil.executor.standard; - - import ca.spottedleaf.concurrentutil.util.ConcurrentUtil; - import com.mojang.logging.LogUtils; -+import org.galemc.gale.executor.chunksystem.AbstractPrioritisedQueueExecutorThreadBase; - import org.slf4j.Logger; - import java.lang.invoke.VarHandle; - import java.util.concurrent.locks.LockSupport; -@@ -15,7 +16,7 @@ import java.util.concurrent.locks.LockSupport; - * methods. - *

    - */ --public class PrioritisedQueueExecutorThread extends Thread implements PrioritisedExecutor { -+public class PrioritisedQueueExecutorThread extends Thread implements AbstractPrioritisedQueueExecutorThreadBase { - - private static final Logger LOGGER = LogUtils.getLogger(); - -@@ -99,28 +100,6 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise - } - } - -- protected boolean pollTasks() { -- boolean ret = false; -- -- for (;;) { -- if (this.halted) { -- break; -- } -- try { -- if (!this.queue.executeTask()) { -- break; -- } -- ret = true; -- } catch (final ThreadDeath death) { -- throw death; // goodbye world... -- } catch (final Throwable throwable) { -- LOGGER.error("Exception thrown from prioritized runnable task in thread '" + this.getName() + "'", throwable); -- } -- } -- -- return ret; -- } -- - protected boolean handleClose() { - if (this.threadShutdown) { - this.pollTasks(); // this ensures we've emptied the queue -@@ -141,12 +120,18 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise - return false; - } - -- @Override -- public PrioritisedTask createTask(final Runnable task, final Priority priority) { -- final PrioritisedExecutor.PrioritisedTask queueTask = this.queue.createTask(task, priority); -+ // Gale start - base thread pool - chunk worker task queue -+ public static class PrioritisedQueueExecutorThreadTask implements PrioritisedTask { -+ -+ private final PrioritisedExecutor.PrioritisedTask queueTask; -+ private final AbstractPrioritisedQueueExecutorThreadBase owningThreadBase; -+ -+ public PrioritisedQueueExecutorThreadTask(PrioritisedExecutor.PrioritisedTask queueTask, AbstractPrioritisedQueueExecutorThreadBase owningThreadBase) { -+ this.queueTask = queueTask; -+ this.owningThreadBase = owningThreadBase; -+ } -+ // Gale end - base thread pool - chunk worker task queue - -- // need to override queue() to notify us of tasks -- return new PrioritisedTask() { - @Override - public Priority getPriority() { - return queueTask.getPriority(); -@@ -171,7 +156,7 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise - public boolean queue() { - final boolean ret = queueTask.queue(); - if (ret) { -- PrioritisedQueueExecutorThread.this.notifyTasks(); -+ this.owningThreadBase.notifyOfTasks(); // Gale - base thread pool - chunk worker task queue - } - return ret; - } -@@ -185,52 +170,7 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise - public boolean execute() { - return queueTask.execute(); - } -- }; -- } -- -- @Override -- public PrioritisedExecutor.PrioritisedTask queueRunnable(final Runnable task, final PrioritisedExecutor.Priority priority) { -- final PrioritisedExecutor.PrioritisedTask ret = this.queue.queueRunnable(task, priority); -- -- this.notifyTasks(); -- -- return ret; -- } - -- @Override -- public boolean haveAllTasksExecuted() { -- return this.queue.haveAllTasksExecuted(); -- } -- -- @Override -- public long getTotalTasksExecuted() { -- return this.queue.getTotalTasksExecuted(); -- } -- -- @Override -- public long getTotalTasksScheduled() { -- return this.queue.getTotalTasksScheduled(); -- } -- -- /** -- * {@inheritDoc} -- * @throws IllegalStateException If the current thread is {@code this} thread, or the underlying queue throws this exception. -- */ -- @Override -- public void waitUntilAllExecuted() throws IllegalStateException { -- if (Thread.currentThread() == this) { -- throw new IllegalStateException("Cannot block on our own queue"); -- } -- this.queue.waitUntilAllExecuted(); -- } -- -- /** -- * {@inheritDoc} -- * @throws IllegalStateException Always -- */ -- @Override -- public boolean executeTask() throws IllegalStateException { -- throw new IllegalStateException(); - } - - /** -@@ -294,4 +234,27 @@ public class PrioritisedQueueExecutorThread extends Thread implements Prioritise - protected final void setThreadParkedVolatile(final boolean value) { - THREAD_PARKED_HANDLE.setVolatile(this, value); - } -+ -+ // Gale start -+ @Override -+ public void notifyOfTasks() { -+ this.notifyTasks(); -+ } -+ -+ @Override -+ public PrioritisedExecutor getQueue() { -+ return this.queue; -+ } -+ -+ @Override -+ public boolean isHalted() { -+ return this.halted; -+ } -+ -+ @Override -+ public Logger getLogger() { -+ return LOGGER; -+ } -+ // Gale end -+ - } -diff --git a/src/main/java/org/galemc/gale/executor/chunksystem/AbstractPrioritisedQueueExecutorThreadBase.java b/src/main/java/org/galemc/gale/executor/chunksystem/AbstractPrioritisedQueueExecutorThreadBase.java -new file mode 100644 -index 0000000000000000000000000000000000000000..37048a100e48f0d103ee8454c7c8c58b25f04c98 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/chunksystem/AbstractPrioritisedQueueExecutorThreadBase.java -@@ -0,0 +1,98 @@ -+// Gale - base thread pool - chunk worker task queue -+ -+package org.galemc.gale.executor.chunksystem; -+ -+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; -+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedQueueExecutorThread; -+import org.slf4j.Logger; -+ -+/** -+ * A common abstract base for {@link PrioritisedQueueExecutorThread} and -+ * {@link PrioritisedQueueExecutorThreadAgent}, based on {@link PrioritisedQueueExecutorThread}. -+ */ -+public interface AbstractPrioritisedQueueExecutorThreadBase extends PrioritisedExecutor { -+ -+ void notifyOfTasks(); -+ -+ PrioritisedExecutor getQueue(); -+ -+ boolean isHalted(); -+ -+ Logger getLogger(); -+ -+ String getName(); -+ -+ default boolean pollTasks() { -+ boolean ret = false; -+ -+ var queue = this.getQueue(); -+ for (;;) { -+ if (this.isHalted()) { -+ break; -+ } -+ try { -+ if (!queue.executeTask()) { -+ break; -+ } -+ ret = true; -+ } catch (final ThreadDeath death) { -+ throw death; // goodbye world... -+ } catch (final Throwable throwable) { -+ this.getLogger().error("Exception thrown from prioritized runnable task in thread '" + this.getName() + "'", throwable); -+ } -+ } -+ -+ return ret; -+ } -+ -+ @Override -+ default PrioritisedTask createTask(final Runnable task, final Priority priority) { -+ final PrioritisedExecutor.PrioritisedTask queueTask = this.getQueue().createTask(task, priority); -+ -+ // need to override queue() to notify us of tasks -+ return new PrioritisedQueueExecutorThread.PrioritisedQueueExecutorThreadTask(queueTask, this); -+ } -+ -+ @Override -+ default PrioritisedTask queueRunnable(final Runnable task, final Priority priority) { -+ final PrioritisedTask ret = this.getQueue().queueRunnable(task, priority); -+ -+ this.notifyOfTasks(); -+ -+ return ret; -+ } -+ -+ @Override -+ default boolean haveAllTasksExecuted() { -+ return this.getQueue().haveAllTasksExecuted(); -+ } -+ -+ @Override -+ default long getTotalTasksExecuted() { -+ return this.getQueue().getTotalTasksExecuted(); -+ } -+ -+ @Override -+ default long getTotalTasksScheduled() { -+ return this.getQueue().getTotalTasksScheduled(); -+ } -+ -+ /** -+ * {@inheritDoc} -+ * @throws IllegalStateException If the current thread is {@code this} thread, or the underlying queue throws this exception. -+ */ -+ @Override -+ default void waitUntilAllExecuted() throws IllegalStateException { -+ this.getQueue().waitUntilAllExecuted(); -+ } -+ -+ /** -+ * {@inheritDoc} -+ * @throws IllegalStateException Always -+ */ -+ @Override -+ default boolean executeTask() throws IllegalStateException { -+ throw new IllegalStateException(); -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/chunksystem/PrioritisedQueueExecutorThreadAgent.java b/src/main/java/org/galemc/gale/executor/chunksystem/PrioritisedQueueExecutorThreadAgent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..abdec5529763b77126494ae0c2be9b48de900bc1 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/chunksystem/PrioritisedQueueExecutorThreadAgent.java -@@ -0,0 +1,55 @@ -+// Gale - base thread pool - chunk worker task queue -+ -+package org.galemc.gale.executor.chunksystem; -+ -+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor; -+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedQueueExecutorThread; -+import com.mojang.logging.LogUtils; -+import org.galemc.gale.executor.queue.BaseTaskQueues; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.slf4j.Logger; -+ -+ -+/** -+ * This class is a copy of {@link PrioritisedQueueExecutorThread}, with the notable difference -+ * that it does not extend {@link Thread}, but may be instantiated on its own, as an agent representing -+ * a {@link BaseThread} to the {@link PrioritisedExecutor}. -+ */ -+public abstract class PrioritisedQueueExecutorThreadAgent implements AbstractPrioritisedQueueExecutorThreadBase { -+ -+ private static final Logger LOGGER = LogUtils.getLogger(); -+ -+ protected final PrioritisedExecutor queue; -+ protected final BaseThread baseThread; -+ -+ public PrioritisedQueueExecutorThreadAgent(final PrioritisedExecutor queue, final BaseThread baseThread) { -+ this.queue = queue; -+ this.baseThread = baseThread; -+ } -+ -+ @Override -+ public void notifyOfTasks() { -+ BaseTaskQueues.chunkWorker.newTaskWasAdded(); -+ } -+ -+ @Override -+ public PrioritisedExecutor getQueue() { -+ return this.queue; -+ } -+ -+ @Override -+ public boolean isHalted() { -+ return false; -+ } -+ -+ @Override -+ public Logger getLogger() { -+ return LOGGER; -+ } -+ -+ @Override -+ public String getName() { -+ return this.baseThread.getName(); -+ } -+ -+} diff --git a/patches/server/0165-Run-chunk-worker-tasks-on-base-thread-pool.patch b/patches/server/0165-Run-chunk-worker-tasks-on-base-thread-pool.patch deleted file mode 100644 index 7f265e5..0000000 --- a/patches/server/0165-Run-chunk-worker-tasks-on-base-thread-pool.patch +++ /dev/null @@ -1,712 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Mon, 30 Jan 2023 22:35:08 +0100 -Subject: [PATCH] Run chunk worker tasks on base thread pool - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadPool.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadPool.java -index 26fa2caa18a9194e57574a4a7fa9f7a4265740e0..2fa56c06ec16817d5e35d5283b8e5a0d0e01d643 100644 ---- a/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadPool.java -+++ b/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadPool.java -@@ -2,11 +2,17 @@ package ca.spottedleaf.concurrentutil.executor.standard; - - import com.mojang.logging.LogUtils; - import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; -+import org.galemc.gale.concurrent.Mutex; -+import org.galemc.gale.executor.chunksystem.PrioritisedQueueExecutorThreadAgent; -+import org.galemc.gale.executor.queue.BaseTaskQueueTier; -+import org.galemc.gale.executor.queue.BaseTaskQueues; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.pool.BaseThreadActivation; -+import org.jetbrains.annotations.NotNull; - import org.slf4j.Logger; --import java.util.ArrayList; --import java.util.Arrays; --import java.util.Comparator; --import java.util.TreeSet; -+ -+import java.util.*; -+import java.util.concurrent.ConcurrentSkipListSet; - import java.util.concurrent.atomic.AtomicBoolean; - import java.util.function.BiConsumer; - -@@ -14,13 +20,16 @@ public final class PrioritisedThreadPool { - - private static final Logger LOGGER = LogUtils.getLogger(); - -- protected final PrioritisedThread[] threads; -- protected final TreeSet queues = new TreeSet<>(PrioritisedPoolExecutorImpl.comparator()); -+ public final Set activeThreads = new ConcurrentSkipListSet<>(); // Gale - base thread pool - chunk worker task queue -+ public final TreeSet queues = new TreeSet<>(PrioritisedPoolExecutorImpl.comparator()); // Gale - base thread pool - chunk worker task queue - protected -> public -+ public final Mutex queuesLock = Mutex.create(); // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - protected final String name; - protected final long queueMaxHoldTime; - - protected final ReferenceOpenHashSet nonShutdownQueues = new ReferenceOpenHashSet<>(); -+ protected final Mutex nonShutdownQueuesLock = Mutex.create(); // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - protected final ReferenceOpenHashSet activeQueues = new ReferenceOpenHashSet<>(); -+ protected final Mutex activeQueuesLock = Mutex.create(); // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - - protected boolean shutdown; - -@@ -46,41 +55,18 @@ public final class PrioritisedThreadPool { - } - this.name = name; - this.queueMaxHoldTime = queueHoldTime; -- -- this.threads = new PrioritisedThread[threads]; -- for (int i = 0; i < threads; ++i) { -- this.threads[i] = new PrioritisedThread(this); -- -- // set default attributes -- this.threads[i].setName("Prioritised thread for pool '" + name + "' #" + i); -- this.threads[i].setUncaughtExceptionHandler((final Thread thread, final Throwable throwable) -> { -- LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable); -- }); -- -- // let thread modifier override defaults -- if (threadModifier != null) { -- threadModifier.accept(this.threads[i], Integer.valueOf(i)); -- } -- -- // now the thread can start -- this.threads[i].start(); -- } - } - -- public Thread[] getThreads() { -- return Arrays.copyOf(this.threads, this.threads.length, Thread[].class); -- } -- -- public PrioritisedPoolExecutor createExecutor(final String name, final int parallelism) { -- synchronized (this.nonShutdownQueues) { -+ public PrioritisedPoolExecutor createExecutor(final String name, boolean supportsParallelism) { // Gale - base thread pool - chunk worker task queue -+ try (var ignored = nonShutdownQueuesLock.withSpinLock()) { // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - if (this.shutdown) { - throw new IllegalStateException("Queue is shutdown: " + this.toString()); - } -- final PrioritisedPoolExecutorImpl ret = new PrioritisedPoolExecutorImpl(this, name, Math.min(Math.max(1, parallelism), this.threads.length)); -+ final PrioritisedPoolExecutorImpl ret = new PrioritisedPoolExecutorImpl(this, name, supportsParallelism ? -1 : 1); // Gale - base thread pool - chunk worker task queue - - this.nonShutdownQueues.add(ret); - -- synchronized (this.activeQueues) { -+ try (var ignored2 = this.activeQueuesLock.withSpinLock()) { // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - this.activeQueues.add(ret); - } - -@@ -88,136 +74,24 @@ public final class PrioritisedThreadPool { - } - } - -- /** -- * Prevents creation of new queues, shutdowns all non-shutdown queues if specified -- */ -- public void halt(final boolean shutdownQueues) { -- synchronized (this.nonShutdownQueues) { -- this.shutdown = true; -- } -- if (shutdownQueues) { -- final ArrayList queuesToShutdown; -- synchronized (this.nonShutdownQueues) { -- this.shutdown = true; -- queuesToShutdown = new ArrayList<>(this.nonShutdownQueues); -- } -- -- for (final PrioritisedPoolExecutorImpl queue : queuesToShutdown) { -- queue.shutdown(); -- } -- } -- -- -- for (final PrioritisedThread thread : this.threads) { -- // can't kill queue, queue is null -- thread.halt(false); -- } -- } -- -- /** -- * Waits until all threads in this pool have shutdown, or until the specified time has passed. -- * @param msToWait Maximum time to wait. -- * @return {@code false} if the maximum time passed, {@code true} otherwise. -- */ -- public boolean join(final long msToWait) { -- try { -- return this.join(msToWait, false); -- } catch (final InterruptedException ex) { -- throw new IllegalStateException(ex); -- } -- } -- -- /** -- * Waits until all threads in this pool have shutdown, or until the specified time has passed. -- * @param msToWait Maximum time to wait. -- * @return {@code false} if the maximum time passed, {@code true} otherwise. -- * @throws InterruptedException If this thread is interrupted. -- */ -- public boolean joinInterruptable(final long msToWait) throws InterruptedException { -- return this.join(msToWait, true); -- } -- -- protected final boolean join(final long msToWait, final boolean interruptable) throws InterruptedException { -- final long nsToWait = msToWait * (1000 * 1000); -- final long start = System.nanoTime(); -- final long deadline = start + nsToWait; -- boolean interrupted = false; -- try { -- for (final PrioritisedThread thread : this.threads) { -- for (;;) { -- if (!thread.isAlive()) { -- break; -- } -- final long current = System.nanoTime(); -- if (current >= deadline) { -- return false; -- } -- -- try { -- thread.join(Math.max(1L, (deadline - current) / (1000 * 1000))); -- } catch (final InterruptedException ex) { -- if (interruptable) { -- throw ex; -- } -- interrupted = true; -- } -- } -- } -- -- return true; -- } finally { -- if (interrupted) { -- Thread.currentThread().interrupt(); -- } -- } -- } -- -- public void shutdown(final boolean wait) { -- final ArrayList queuesToShutdown; -- synchronized (this.nonShutdownQueues) { -- this.shutdown = true; -- queuesToShutdown = new ArrayList<>(this.nonShutdownQueues); -- } -- -- for (final PrioritisedPoolExecutorImpl queue : queuesToShutdown) { -- queue.shutdown(); -- } -- -- for (final PrioritisedThread thread : this.threads) { -- // none of these can be true or else NPE -- thread.close(false, false); -- } -- -- if (wait) { -- final ArrayList queues; -- synchronized (this.activeQueues) { -- queues = new ArrayList<>(this.activeQueues); -- } -- for (final PrioritisedPoolExecutorImpl queue : queues) { -- queue.waitUntilAllExecuted(); -- } -- } -- } -- -- protected static final class PrioritisedThread extends PrioritisedQueueExecutorThread { -+ public static final class PrioritisedThreadAgent extends PrioritisedQueueExecutorThreadAgent implements Comparable { // Gale - base thread pool - chunk worker task queue - - protected final PrioritisedThreadPool pool; - protected final AtomicBoolean alertedHighPriority = new AtomicBoolean(); - -- public PrioritisedThread(final PrioritisedThreadPool pool) { -- super(null); -+ // Gale start - base thread pool - chunk worker task queue -+ public PrioritisedThreadAgent(final PrioritisedThreadPool pool, BaseThread baseThread) { -+ // Gale end - base thread pool - chunk worker task queue -+ super(null, baseThread); - this.pool = pool; - } - -- public boolean alertHighPriorityExecutor() { -- if (!this.notifyTasks()) { -- if (!this.alertedHighPriority.get()) { -- this.alertedHighPriority.set(true); -- } -- return false; -+ // Gale start - base thread pool - chunk worker task queue -+ public void alertHighPriorityExecutor() { -+ if (!this.alertedHighPriority.get()) { -+ this.alertedHighPriority.set(true); - } -- -- return true; -+ // Gale end - base thread pool - chunk worker task queue - } - - private boolean isAlertedHighPriority() { -@@ -225,21 +99,19 @@ public final class PrioritisedThreadPool { - } - - @Override -- protected boolean pollTasks() { -+ public boolean pollTasks() { // Gale - base thread pool - chunk worker task queue - protected -> public - final PrioritisedThreadPool pool = this.pool; - final TreeSet queues = this.pool.queues; -+ final Mutex queuesLock = this.pool.queuesLock; // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - - boolean ret = false; - for (;;) { -- if (this.halted) { -- break; -- } - // try to find a queue - // note that if and ONLY IF the queues set is empty, this means there are no tasks for us to execute. - // so we can only break when it's empty - final PrioritisedPoolExecutorImpl queue; - // select queue -- synchronized (queues) { -+ try (var ignored = queuesLock.withSpinLock()) { // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - queue = queues.pollFirst(); - if (queue == null) { - // no tasks to execute -@@ -249,7 +121,7 @@ public final class PrioritisedThreadPool { - queue.schedulingId = ++pool.schedulingIdGenerator; - // we own this queue now, so increment the executor count - // do we also need to push this queue up for grabs for another executor? -- if (++queue.concurrentExecutors < queue.maximumExecutors) { -+ if (++queue.concurrentExecutors < queue.maximumExecutors || queue.maximumExecutors == -1) { // Gale - base thread pool - chunk worker task queue - // re-add to queues - // it's very important this is done in the same synchronised block for polling, as this prevents - // us from possibly later adding a queue that should not exist in the set -@@ -268,9 +140,6 @@ public final class PrioritisedThreadPool { - final long deadline = start + pool.queueMaxHoldTime; - do { - try { -- if (this.halted) { -- break; -- } - if (!queue.executeTask()) { - // no more tasks, try next queue - break; -@@ -279,11 +148,11 @@ public final class PrioritisedThreadPool { - } catch (final ThreadDeath death) { - throw death; // goodbye world... - } catch (final Throwable throwable) { -- LOGGER.error("Exception thrown from thread '" + this.getName() + "' in queue '" + queue.toString() + "'", throwable); -+ LOGGER.error("Exception thrown from thread '" + this.baseThread.getName() + "' in queue '" + queue.toString() + "'", throwable); - } -- } while (!this.isAlertedHighPriority() && System.nanoTime() <= deadline); -+ } while (!this.isAlertedHighPriority() && System.nanoTime() <= deadline && BaseThreadActivation.tierInExcessOrdinal > BaseTaskQueueTier.LOW_PRIORITY_ASYNC.ordinal); // Gale - base thread pool - chunk worker task queue - -- synchronized (queues) { -+ try (var ignored = queuesLock.withSpinLock()) { // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - // decrement executors, we are no longer executing - if (queue.isQueued) { - queues.remove(queue); -@@ -306,6 +175,12 @@ public final class PrioritisedThreadPool { - - return ret; - } -+ -+ @Override -+ public int compareTo(@NotNull PrioritisedThreadPool.PrioritisedThreadAgent o) { -+ return Integer.compare(this.baseThread.baseThreadIndex, o.baseThread.baseThreadIndex); -+ } -+ - } - - public interface PrioritisedPoolExecutor extends PrioritisedExecutor { -@@ -322,7 +197,7 @@ public final class PrioritisedThreadPool { - public boolean isActive(); - } - -- protected static final class PrioritisedPoolExecutorImpl extends PrioritisedThreadedTaskQueue implements PrioritisedPoolExecutor { -+ public static final class PrioritisedPoolExecutorImpl extends PrioritisedThreadedTaskQueue implements PrioritisedPoolExecutor { // Gale - base thread pool - chunk worker task queue - protected -> public - - protected final PrioritisedThreadPool pool; - protected final long[] priorityCounts = new long[Priority.TOTAL_SCHEDULABLE_PRIORITIES]; -@@ -369,7 +244,10 @@ public final class PrioritisedThreadPool { - public void halt() { - final PrioritisedThreadPool pool = this.pool; - final TreeSet queues = pool.queues; -- synchronized (queues) { -+ // Gale start - base thread pool - chunk worker task queue - spin lock for pool queues -+ final Mutex queuesLock = pool.queuesLock; -+ try (var ignored = queuesLock.withSpinLock()) { -+ // Gale end - base thread pool - chunk worker task queue - spin lock for pool queues - if (this.isHalted) { - return; - } -@@ -379,10 +257,10 @@ public final class PrioritisedThreadPool { - this.isQueued = false; - } - } -- synchronized (pool.nonShutdownQueues) { -+ try (var ignored = pool.nonShutdownQueuesLock.withSpinLock()) { // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - pool.nonShutdownQueues.remove(this); - } -- synchronized (pool.activeQueues) { -+ try (var ignored = pool.activeQueuesLock.withSpinLock()) { // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - pool.activeQueues.remove(this); - } - } -@@ -391,12 +269,15 @@ public final class PrioritisedThreadPool { - public boolean isActive() { - final PrioritisedThreadPool pool = this.pool; - final TreeSet queues = pool.queues; -+ // Gale start - base thread pool - chunk worker task queue - spin lock for pool queues -+ final Mutex queuesLock = pool.queuesLock; - -- synchronized (queues) { -+ try (var ignored = queuesLock.withSpinLock()) { -+ // Gale end - base thread pool - chunk worker task queue - spin lock for pool queues - if (this.concurrentExecutors != 0) { - return true; - } -- synchronized (pool.activeQueues) { -+ try (var ignored2 = pool.activeQueuesLock.withSpinLock()) { // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - if (pool.activeQueues.contains(this)) { - return true; - } -@@ -469,8 +350,11 @@ public final class PrioritisedThreadPool { - - final PrioritisedThreadPool pool = this.pool; - final TreeSet queues = pool.queues; -+ // Gale start - base thread pool - chunk worker task queue - spin lock for pool queues -+ final Mutex queuesLock = pool.queuesLock; - -- synchronized (queues) { -+ try (var ignored = queuesLock.withSpinLock()) { -+ // Gale end - base thread pool - chunk worker task queue - spin lock for pool queues - if (!this.isQueued) { - // see if we need to be queued - if (newPriority != null) { -@@ -478,7 +362,7 @@ public final class PrioritisedThreadPool { - this.schedulingId = ++pool.schedulingIdGenerator; - } - this.scheduledPriority = newPriority; // must be updated before queue add -- if (!this.isHalted && this.concurrentExecutors < this.maximumExecutors) { -+ if (!this.isHalted && (this.concurrentExecutors < this.maximumExecutors || this.maximumExecutors == -1)) { // Gale - base thread pool - chunk worker task queue - shouldNotifyHighPriority = newPriority.isHigherOrEqualPriority(Priority.HIGH); - queues.add(this); - this.isQueued = true; -@@ -511,19 +395,20 @@ public final class PrioritisedThreadPool { - } - - if (newPriority == null && shutdown) { -- synchronized (pool.activeQueues) { -+ try (var ignored = pool.activeQueuesLock.withSpinLock()) { // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - pool.activeQueues.remove(this); - } - } - - // Wake up the number of executors we want -+ // Gale start - base thread pool - chunk worker task queue - if (executorsWanted > 0 || (shouldNotifyTasks | shouldNotifyHighPriority)) { -- int notified = 0; -- for (final PrioritisedThread thread : pool.threads) { -- if ((shouldNotifyHighPriority ? thread.alertHighPriorityExecutor() : thread.notifyTasks()) -- && (++notified >= executorsWanted)) { -- break; -+ BaseTaskQueues.chunkWorker.newTaskWasAdded(); -+ if (shouldNotifyHighPriority) { -+ for (final PrioritisedThreadAgent thread : pool.activeThreads) { -+ thread.alertHighPriorityExecutor(); - } -+ // Gale end - base thread pool - chunk worker task queue - } - } - } -@@ -538,17 +423,18 @@ public final class PrioritisedThreadPool { - final PrioritisedThreadPool pool = this.pool; - - // remove from active queues -- synchronized (pool.nonShutdownQueues) { -+ try (var ignored = pool.nonShutdownQueuesLock.withSpinLock()) { // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - pool.nonShutdownQueues.remove(this); - } - - final TreeSet queues = pool.queues; -+ final Mutex queuesLock = pool.queuesLock; // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - - // try and shift around our priority -- synchronized (queues) { -+ try (var ignored = queuesLock.withSpinLock()) { // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - if (this.scheduledPriority == null) { - // no tasks are queued, ensure we aren't in activeQueues -- synchronized (pool.activeQueues) { -+ try (var ignored2 = pool.activeQueuesLock.withSpinLock()) { // Gale - base thread pool - chunk worker task queue - spin lock for pool queues - pool.activeQueues.remove(this); - } - -diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java -index f5c15d40094c2ddc6220b0595597d12103fcf425..79ef41d2bb30beee2355d1de3dc99c9e00d929d5 100644 ---- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java -+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java -@@ -22,6 +22,7 @@ import net.minecraft.world.level.chunk.ChunkAccess; - import net.minecraft.world.level.chunk.ChunkStatus; - import net.minecraft.world.level.chunk.LevelChunk; - import org.bukkit.Bukkit; -+import org.galemc.gale.executor.thread.pool.BaseThreadPool; - import org.slf4j.Logger; - import java.io.File; - import java.util.ArrayDeque; -@@ -103,7 +104,7 @@ public final class ChunkTaskScheduler { - thread.setUncaughtExceptionHandler(io.papermc.paper.chunk.system.scheduling.NewChunkHolder.CHUNKSYSTEM_UNCAUGHT_EXCEPTION_HANDLER); - }, (long)(20.0e6)); // 20ms - -- LOGGER.info("Chunk system is using " + newChunkSystemIOThreads + " I/O threads, " + newChunkSystemWorkerThreads + " worker threads, and gen parallelism of " + ChunkTaskScheduler.newChunkSystemGenParallelism + " threads"); -+ LOGGER.info("Chunk system is using " + newChunkSystemIOThreads + " I/O thread" + (newChunkSystemIOThreads == 1 ? "" : "s")); // Gale - base thread pool - chunk worker task queue - } - - public final ServerLevel world; -@@ -196,12 +197,14 @@ public final class ChunkTaskScheduler { - this.workers = workers; - - final String worldName = world.getWorld().getName(); -- this.genExecutor = workers.createExecutor("Chunk single-threaded generation executor for world '" + worldName + "'", 1); -+ this.genExecutor = workers.createExecutor("Chunk single-threaded generation executor for world '" + worldName + "'", false); // Gale - base thread pool - chunk worker task queue - // same as genExecutor, as there are race conditions between updating blocks in FEATURE status while lighting chunks - this.lightExecutor = this.genExecutor; -- this.parallelGenExecutor = newChunkSystemGenParallelism <= 1 ? this.genExecutor -- : workers.createExecutor("Chunk parallel generation executor for world '" + worldName + "'", newChunkSystemGenParallelism); -- this.loadExecutor = workers.createExecutor("Chunk load executor for world '" + worldName + "'", newChunkSystemLoadParallelism); -+ // Gale start - base thread pool - chunk worker task queue -+ this.parallelGenExecutor = BaseThreadPool.targetParallelism <= 2 ? this.genExecutor -+ : workers.createExecutor("Chunk parallel generation executor for world '" + worldName + "'", true); -+ this.loadExecutor = workers.createExecutor("Chunk load executor for world '" + worldName + "'", true); -+ // Gale end - base thread pool - chunk worker task queue - this.chunkHolderManager = new ChunkHolderManager(world, this); - } - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index d01c81bed1f77e7f8fec56b01fd7885a0603fd22..6053a158d923d0e3cf7215b95fc3b89a3135bfb2 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -151,6 +151,7 @@ import net.minecraft.world.phys.Vec3; - import org.galemc.gale.executor.MinecraftServerBlockableEventLoop; - import org.galemc.gale.configuration.GaleConfigurations; - import org.galemc.gale.executor.annotation.thread.OriginalServerThreadOnly; -+import org.galemc.gale.executor.queue.BaseTaskQueues; - import org.galemc.gale.executor.queue.ScheduledServerThreadTaskQueues; - import org.galemc.gale.executor.thread.BaseThread; - import org.galemc.gale.executor.thread.OriginalServerThread; -@@ -1071,6 +1072,10 @@ public abstract class MinecraftServer extends MinecraftServerBlockableEventLoop - } - } - -+ // Gale start - base thread pool - chunk worker task queue -+ LOGGER.info("Waiting for chunk worker tasks to finish..."); -+ serverThread.runTasksUntil(null, () -> !BaseTaskQueues.chunkWorker.hasTasks(), null); -+ // Gale end - base thread pool - chunk worker task queue - this.saveAllChunks(false, true, false, true); // Paper - rewrite chunk system - move closing into here - - this.isSaving = false; -diff --git a/src/main/java/org/galemc/gale/executor/chunksystem/PrioritisedQueueExecutorThreadAgent.java b/src/main/java/org/galemc/gale/executor/chunksystem/PrioritisedQueueExecutorThreadAgent.java -index abdec5529763b77126494ae0c2be9b48de900bc1..35233587de14cf52da30324df89d9ec76b9c09fe 100644 ---- a/src/main/java/org/galemc/gale/executor/chunksystem/PrioritisedQueueExecutorThreadAgent.java -+++ b/src/main/java/org/galemc/gale/executor/chunksystem/PrioritisedQueueExecutorThreadAgent.java -@@ -9,7 +9,6 @@ import org.galemc.gale.executor.queue.BaseTaskQueues; - import org.galemc.gale.executor.thread.BaseThread; - import org.slf4j.Logger; - -- - /** - * This class is a copy of {@link PrioritisedQueueExecutorThread}, with the notable difference - * that it does not extend {@link Thread}, but may be instantiated on its own, as an agent representing -diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -index 0035d638667c6e0707ecf3e3c040f0123f8e68d5..afd25cb20200baea7c62cf6b3081e19e4188997a 100644 ---- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java -@@ -75,7 +75,6 @@ public enum BaseTaskQueueTier { - /** - * A tier for queues that contain general tasks that must be performed at some point in time, - * asynchronously with respect to the {@link ServerThread} and the ticking of the server. -- * Execution of - */ - ASYNC(new AbstractTaskQueue[]{ - // The cleaner queue has high priority because it releases resources back to a pool, thereby saving memory -@@ -86,7 +85,9 @@ public enum BaseTaskQueueTier { - * A tier for queues that contain tasks with the same considerations as {@link #ASYNC}, - * but with a low priority. - */ -- LOW_PRIORITY_ASYNC(new AbstractTaskQueue[0], Integer.getInteger("gale.thread.priority.async.low", 3)); -+ LOW_PRIORITY_ASYNC(new AbstractTaskQueue[]{ -+ BaseTaskQueues.chunkWorker -+ }, Integer.getInteger("gale.thread.priority.async.low", 3)); - - /** - * Equal to {@link #ordinal()}. -diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -index ed3ccf2e64539363a7be2d507c68c40b5913f75c..a12250e5aaed02995b7bf09a8018a93f27e42920 100644 ---- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java -@@ -113,4 +113,9 @@ public final class BaseTaskQueues { - */ - public static final SimpleTaskQueue scheduledAsync = SimpleTaskQueue.allSpans("ScheduledAsync"); - -+ /** -+ * @see ChunkWorkerTaskQueue -+ */ -+ public static final ChunkWorkerTaskQueue chunkWorker = new ChunkWorkerTaskQueue(); -+ - } -diff --git a/src/main/java/org/galemc/gale/executor/queue/ChunkWorkerTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/ChunkWorkerTaskQueue.java -new file mode 100644 -index 0000000000000000000000000000000000000000..0fac4a8b238636adb826a3d1a8db204042fb8098 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/executor/queue/ChunkWorkerTaskQueue.java -@@ -0,0 +1,111 @@ -+// Gale - base thread pool - chunk worker task queue -+ -+package org.galemc.gale.executor.queue; -+ -+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedQueueExecutorThread; -+import io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler; -+import org.galemc.gale.executor.TaskSpan; -+import org.galemc.gale.executor.annotation.YieldFree; -+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; -+import org.galemc.gale.executor.thread.BaseThread; -+import org.galemc.gale.executor.thread.pool.BaseThreadActivation; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * This class provides access to, but does not store, the tasks related to chunk loading and chunk generation, -+ * that are scheduled and normally polled by the chunk system's {@link PrioritisedQueueExecutorThread}s. -+ *
    -+ * Tasks provided by this queue are {@link TaskSpan#YIELDING}. // TODO Gale replace potential used synchronized blocks etc. by spin locks or yielding locks -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+@AnyThreadSafe -+@YieldFree -+public final class ChunkWorkerTaskQueue implements AbstractTaskQueue { -+ -+ /** -+ * Will be initialized in {@link #setTier}. -+ */ -+ private BaseTaskQueueTier tier; -+ -+ ChunkWorkerTaskQueue() {} -+ -+ @Override -+ public String getName() { -+ return "ChunkWorker"; -+ } -+ -+ @Override -+ public boolean canBeYieldedTo() { -+ /* -+ A single returned chunk worker task typically performs a loop that executes as many smaller tasks as possible, -+ and which may keep running for a long time. -+ */ -+ return false; -+ } -+ -+ @Override -+ public boolean hasTasks() { -+ var workerThreads = ChunkTaskScheduler.workerThreads; -+ if (workerThreads == null) { -+ return false; -+ } -+ try (var ignored = workerThreads.queuesLock.withSpinLock()) { -+ for (var queue : workerThreads.queues) { -+ if (queue.hasScheduledUncompletedTasksVolatile()) { -+ return true; -+ } -+ } -+ } -+ return false; -+ } -+ -+ @Override -+ public boolean hasTasks(TaskSpan span) { -+ return span == TaskSpan.YIELDING && this.hasTasks(); -+ } -+ -+ @Override -+ public boolean canHaveTasks(TaskSpan span) { -+ return span == TaskSpan.YIELDING; -+ } -+ -+ @Override -+ public @Nullable Runnable poll(BaseThread currentThread) { -+ if (!this.hasTasks()) { -+ return null; -+ } -+ return () -> { -+ var workerAgent = currentThread.getChunkWorkerAgent(); -+ ChunkTaskScheduler.workerThreads.activeThreads.add(workerAgent); -+ workerAgent.pollTasks(); -+ ChunkTaskScheduler.workerThreads.activeThreads.remove(workerAgent); -+ }; -+ } -+ -+ @Override -+ public @Nullable Runnable pollTiny(BaseThread currentThread) { -+ return null; -+ } -+ -+ @Override -+ public void add(Runnable task, TaskSpan span) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ /** -+ * To be called when a new task has been added to the underlying storage of this queue. -+ */ -+ public void newTaskWasAdded() { -+ BaseThreadActivation.newTaskWasAdded(this.tier, TaskSpan.YIELDING, false, true); -+ } -+ -+ @Override -+ public void setTier(BaseTaskQueueTier tier) { -+ if (this.tier != null) { -+ throw new IllegalStateException(this.getClass().getSimpleName() + ".tier was already initialized"); -+ } -+ this.tier = tier; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/executor/thread/BaseThread.java b/src/main/java/org/galemc/gale/executor/thread/BaseThread.java -index cb1731f8c00f30a18aa042eaabc7f429fcaf9e2a..ce46283459ee5d9af9d8edd88d11322e354ffeae 100644 ---- a/src/main/java/org/galemc/gale/executor/thread/BaseThread.java -+++ b/src/main/java/org/galemc/gale/executor/thread/BaseThread.java -@@ -2,6 +2,8 @@ - - package org.galemc.gale.executor.thread; - -+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadPool; -+import io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler; - import io.papermc.paper.util.TickThread; - import net.minecraft.server.MinecraftServer; - import org.galemc.gale.executor.TaskSpan; -@@ -17,6 +19,7 @@ import org.galemc.gale.executor.queue.AbstractTaskQueue; - import org.galemc.gale.executor.queue.BaseTaskQueueTier; - import org.galemc.gale.executor.thread.platform.SignalReason; - import org.galemc.gale.executor.thread.pool.*; -+import org.jetbrains.annotations.NotNull; - import org.jetbrains.annotations.Nullable; - - import java.util.concurrent.TimeUnit; -@@ -743,6 +746,20 @@ public abstract class BaseThread extends Thread implements AbstractYieldingThrea - } - } - -+ // Gale start - base thread pool - chunk worker task queue -+ /** -+ * Lazily computed value for {@link #getChunkWorkerAgent}. -+ */ -+ private @Nullable PrioritisedThreadPool.PrioritisedThreadAgent chunkWorkerAgent; -+ -+ public @NotNull PrioritisedThreadPool.PrioritisedThreadAgent getChunkWorkerAgent() { -+ if (this.chunkWorkerAgent == null) { -+ this.chunkWorkerAgent = new PrioritisedThreadPool.PrioritisedThreadAgent(ChunkTaskScheduler.workerThreads, this); -+ } -+ return this.chunkWorkerAgent; -+ } -+ // Gale end - base thread pool - chunk worker task queue -+ - /** - * @return The current thread if it is a {@link BaseThread}, or null otherwise. - */ diff --git a/patches/server/0166-Split-tick-steps.patch b/patches/server/0166-Split-tick-steps.patch deleted file mode 100644 index 3e019fa..0000000 --- a/patches/server/0166-Split-tick-steps.patch +++ /dev/null @@ -1,651 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Tue, 31 Jan 2023 19:05:12 +0100 -Subject: [PATCH] Split tick steps - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 6053a158d923d0e3cf7215b95fc3b89a3135bfb2..5db4312ed1973a2395af66975a43abe5beffa1cd 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1584,16 +1584,54 @@ public abstract class MinecraftServer extends MinecraftServerBlockableEventLoop - } - } - -+ // Gale start - split tick steps -+ public static BooleanSupplier tick_shouldKeepTicking; -+ public static final boolean tick_tickChunks = true; -+ // Gale end - split tick steps -+ - public void tickChildren(BooleanSupplier shouldKeepTicking) { -+ // Gale start - split tick steps -+ tick_shouldKeepTicking = shouldKeepTicking; -+ this.tickStep_doSchedulerHeartbeat(); -+ this.tickStep_tickFunctions(); -+ //Iterator iterator = this.getAllLevels().iterator(); // Paper - moved down -+ this.tickStep_runProcessQueueTasks(); -+ for (final ServerLevel world : this.getAllLevelsArray()) { // Gale - base thread pool - optimize server levels -+ this.tickStep_sendTimeUpdates(world); -+ } -+ this.tickStep_startIteratingOverLevels(); -+ for (ServerLevel worldserver : this.getAllLevelsArray()) { // Paper - move down // Gale - base thread pool - optimize server levels -+ this.tickStep_updateEvents(worldserver); -+ this.tickStep_tickWorld(worldserver); -+ this.tickStep_recalculateRegions(worldserver); -+ this.tickStep_clearExplosionDensityCache(worldserver); -+ } -+ this.tickStep_stopIteratingOverLevels(); -+ this.tickStep_tickConnection(); -+ this.tickStep_tickPlayerList(); -+ this.tickStep_tickGameTestTicker(); -+ this.tickStep_runTickables(); -+ } -+ -+ public final void tickStep_doSchedulerHeartbeat() { -+ // Gale end - split tick steps - MinecraftTimings.bukkitSchedulerTimer.startTiming(); // Spigot // Paper - this.server.getScheduler().mainThreadHeartbeat(this.tickCount); // CraftBukkit - MinecraftTimings.bukkitSchedulerTimer.stopTiming(); // Spigot // Paper - io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper -+ // Gale start - split tick steps -+ } -+ -+ public final void tickStep_tickFunctions() { -+ // Gale end - split tick steps - MinecraftTimings.commandFunctionsTimer.startTiming(); // Spigot // Paper - this.getFunctions().tick(); - MinecraftTimings.commandFunctionsTimer.stopTiming(); // Spigot // Paper -- //Iterator iterator = this.getAllLevels().iterator(); // Paper - moved down -+ // Gale start - split tick steps -+ } - -+ public final void tickStep_runProcessQueueTasks() { -+ // Gale end - split tick steps - // CraftBukkit start - // Run tasks that are waiting on processing - MinecraftTimings.processQueueTimer.startTiming(); // Spigot -@@ -1601,11 +1639,14 @@ public abstract class MinecraftServer extends MinecraftServerBlockableEventLoop - this.processQueue.remove().run(); - } - MinecraftTimings.processQueueTimer.stopTiming(); // Spigot -+ // Gale start - split tick steps -+ } - -+ public final void tickStep_sendTimeUpdates(ServerLevel world) { -+ // Gale end - split tick steps - MinecraftTimings.timeUpdateTimer.startTiming(); // Spigot // Paper - // Send time updates to everyone, it will get the right time from the world the player is in. - // Paper start - optimize time updates -- for (final ServerLevel world : this.getAllLevelsArray()) { // Gale - base thread pool - optimize server levels - final boolean doDaylight = world.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT); - final long dayTime = world.getDayTime(); - long worldTime = world.getGameTime(); -@@ -1620,15 +1661,23 @@ public abstract class MinecraftServer extends MinecraftServerBlockableEventLoop - new ClientboundSetTimePacket(worldTime, playerTime, doDaylight); - entityplayer.connection.send(packet); // Add support for per player time - } -- } - // Paper end - MinecraftTimings.timeUpdateTimer.stopTiming(); // Spigot // Paper -+ // Gale start - split tick steps -+ } - -+ public final void tickStep_startIteratingOverLevels() { -+ // Gale end - split tick steps - this.isIteratingOverLevels = true; // Paper -- for (ServerLevel worldserver : this.getAllLevelsArray()) { // Paper - move down // Gale - base thread pool - optimize server levels -+ // Gale start - split tick steps -+ } -+ -+ public final void tickStep_updateEvents(ServerLevel worldserver) { -+ // Gale end - split tick steps - worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper -+ } // Gale - split tick steps - - /* Drop global time updates - if (this.tickCount % 20 == 0) { -@@ -1638,16 +1687,28 @@ public abstract class MinecraftServer extends MinecraftServerBlockableEventLoop - } - // CraftBukkit end */ - -- try { -- worldserver.timings.doTick.startTiming(); // Spigot -- worldserver.tick(shouldKeepTicking); -+ public final void tickStep_tickWorld(ServerLevel worldserver) { -+ worldserver.tick(tick_shouldKeepTicking); -+ } -+ -+ public final void tickStep_recalculateRegions(ServerLevel worldserver) { -+ try { -+ worldserver.timings.doTick.startTiming(); // Spigot -+ // Gale end - split tick steps - // Paper start - for (final io.papermc.paper.chunk.SingleThreadChunkRegionManager regionManager : worldserver.getChunkSource().chunkMap.regionManagers) { - regionManager.recalculateRegions(); - } -- // Paper end - worldserver.timings.doTick.stopTiming(); // Spigot -- } catch (Throwable throwable) { -+ // Paper end -+ // Gale start - split tick steps -+ } catch (Throwable throwable) { -+ SERVER.catchWorldTickException(worldserver, throwable); -+ } -+ } -+ -+ public final void catchWorldTickException(ServerLevel worldserver, Throwable throwable) { -+ // Gale end - split tick steps - // Spigot Start - CrashReport crashreport; - try { -@@ -1660,22 +1721,47 @@ public abstract class MinecraftServer extends MinecraftServerBlockableEventLoop - - worldserver.fillReportDetails(crashreport); - throw new ReportedException(crashreport); -- } -+ // Gale start - split tick steps -+ } - -+ public final void tickStep_clearExplosionDensityCache(ServerLevel worldserver) { -+ // Gale end - split tick steps - worldserver.explosionDensityCache.clear(); // Paper - Optimize explosions -- } -+ // Gale start - split tick steps -+ } -+ -+ public final void tickStep_stopIteratingOverLevels() { -+ // Gale end - split tick steps - this.isIteratingOverLevels = false; // Paper -+ // Gale start - split tick steps -+ } - -+ public final void tickStep_tickConnection() { -+ // Gale end - split tick steps - MinecraftTimings.connectionTimer.startTiming(); // Spigot - this.getConnection().tick(); - MinecraftTimings.connectionTimer.stopTiming(); // Spigot -+ // Gale start - split tick steps -+ } -+ -+ public final void tickStep_tickPlayerList() { -+ // Gale end - split tick steps - MinecraftTimings.playerListTimer.startTiming(); // Spigot // Paper - this.playerList.tick(); - MinecraftTimings.playerListTimer.stopTiming(); // Spigot // Paper -+ // Gale start - split tick steps -+ } -+ -+ public final void tickStep_tickGameTestTicker() { -+ // Gale end - split tick steps - if (SharedConstants.IS_RUNNING_IN_IDE) { - GameTestTicker.SINGLETON.tick(); - } -+ // Gale start - split tick steps -+ } - -+ public final void tickStep_runTickables() { -+ // Gale end - split tick steps - MinecraftTimings.tickablesTimer.startTiming(); // Spigot // Paper - for (int i = 0; i < this.tickables.size(); ++i) { - ((Runnable) this.tickables.get(i)).run(); -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 9ee931a6442d3f18a7521704f39297af0d7af6d7..d4f99270c62cef94cc5ad5fc00f155c480722516 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -629,23 +629,99 @@ public class ServerChunkCache extends ChunkSource { - } - // CraftBukkit end - -+ // Gale start - split tick steps -+ public Runnable tickStep_purgeStaleTickets; -+ public Runnable tickStep_runDistanceManagerUpdates; -+ public Runnable tickStep_tickChunks; -+ public Runnable tickStep_tickChunkMap; -+ public Runnable tickStep_clearCache; -+ // Gale end - split tick steps -+ - @Override - public void tick(BooleanSupplier shouldKeepTicking, boolean tickChunks) { -+ // Gale start - split tick steps -+ this.initializeTickSteps(); -+ this.tickStep_purgeStaleTickets.run(); -+ this.tickStep_runDistanceManagerUpdates.run(); -+ this.tickStep_tickChunks.run(); -+ this.tickStep_tickChunkMap.run(); -+ this.tickStep_clearCache.run(); -+ } -+ -+ public void initializeTickSteps() { -+ //noinspection ConstantValue -+ if (this.tickStep_purgeStaleTickets != null) { -+ return; -+ } -+ this.tickStep_purgeStaleTickets = this.tickStep_purgeStaleTickets_create(); -+ this.tickStep_runDistanceManagerUpdates = this.tickStep_runDistanceManagerUpdates_create(); -+ this.tickStep_tickChunks = this.tickStep_tickChunks_create(); -+ this.tickStep_tickChunkMap = this.tickStep_tickChunkMap_create(); -+ this.tickStep_clearCache = this.tickStep_clearCache_create(); -+ } -+ -+ private Runnable wrapTickStep(Runnable runnable) { -+ return this.level.wrapTickStep(() -> { -+ this.level.timings.chunkProviderTick.startTiming(); // Paper - timings -+ runnable.run(); -+ this.level.timings.chunkProviderTick.stopTiming(); // Paper - timings -+ }); -+ } -+ -+ private Runnable wrapDoChunkMapTickStep(Runnable runnable) { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - this.level.timings.doChunkMap.startTiming(); // Spigot -+ // Gale start - split tick steps -+ runnable.run(); -+ this.level.timings.doChunkMap.stopTiming(); // Spigot -+ }); -+ } -+ -+ private Runnable tickStep_purgeStaleTickets_create() { -+ return this.wrapDoChunkMapTickStep(() -> { -+ // Gale end - split tick steps - this.distanceManager.purgeStaleTickets(); -+ // Gale start - split tick steps -+ }); -+ } -+ -+ private Runnable tickStep_runDistanceManagerUpdates_create() { -+ return this.wrapDoChunkMapTickStep(() -> { -+ // Gale end - split tick steps - this.runDistanceManagerUpdates(); -- this.level.timings.doChunkMap.stopTiming(); // Spigot -- if (tickChunks) { -+ // Gale start - split tick steps -+ }); -+ } -+ -+ private Runnable tickStep_tickChunks_create() { -+ return this.wrapTickStep(() -> { -+ if (MinecraftServer.tick_tickChunks) { -+ // Gale end - split tick steps - this.level.timings.chunks.startTiming(); // Paper - timings - this.chunkMap.playerChunkManager.tick(); // Paper - this is mostly is to account for view distance changes - this.tickChunks(); - this.level.timings.chunks.stopTiming(); // Paper - timings - } -+ // Gale start - split tick steps -+ }); -+ } - -+ private Runnable tickStep_tickChunkMap_create() { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - this.level.timings.doChunkUnload.startTiming(); // Spigot -- this.chunkMap.tick(shouldKeepTicking); -+ this.chunkMap.tick(MinecraftServer.tick_shouldKeepTicking); // Gale - split tick steps - this.level.timings.doChunkUnload.stopTiming(); // Spigot -+ // Gale start - split tick steps -+ }); -+ } -+ -+ private Runnable tickStep_clearCache_create() { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - this.clearCache(); -+ }); // Gale - split tick steps - } - - private void tickChunks() { -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index a60fdfa682cbce4ed69117beea2d6bd74790d426..35332bc7e1781086829161131692570c812761fa 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -23,7 +23,6 @@ import java.nio.file.Files; - import java.nio.file.Path; - import java.util.ArrayList; - import java.util.Arrays; --import java.util.Collections; - import java.util.Comparator; - import java.util.Iterator; - import java.util.List; -@@ -169,12 +168,10 @@ import org.bukkit.Location; - import org.bukkit.WeatherType; - import org.bukkit.craftbukkit.event.CraftEventFactory; - import org.bukkit.craftbukkit.generator.CustomWorldChunkManager; --import org.bukkit.craftbukkit.util.CraftNamespacedKey; - import org.bukkit.craftbukkit.util.WorldUUID; - import org.bukkit.event.entity.CreatureSpawnEvent; - import org.bukkit.event.server.MapInitializeEvent; - import org.bukkit.event.weather.LightningStrikeEvent; --import org.bukkit.event.world.GenericGameEvent; - import org.bukkit.event.world.TimeSkipEvent; - // CraftBukkit end - import it.unimi.dsi.fastutil.ints.IntArrayList; // Paper -@@ -633,7 +630,101 @@ public class ServerLevel extends Level implements WorldGenLevel { - return this.structureManager; - } - -+ // Gale start - split tick steps -+ public Runnable tickStep_updatePlayersAffectingSpawning; -+ public Runnable tickStep_startHandlingTick; -+ public Runnable tickStep_tickWorldBorder; -+ public Runnable tickStep_advanceWeatherCycle; -+ public Runnable tickStep_applySleep; -+ public Runnable tickStep_updateSkyBrightness; -+ public Runnable tickStep_tickTime; -+ public Runnable tickStep_setScheduledBlocksGameTime; -+ public Runnable tickStep_setIsDebug; -+ public Runnable tickStep_tickBlocks; -+ public Runnable tickStep_tickFluids; -+ public Runnable tickStep_tickRaids; -+ public Runnable tickStep_tickChunkSource; -+ public Runnable tickStep_doRunBlockEvents; -+ public Runnable tickStep_stopHandlingTick; -+ public Runnable tickStep_setDoEntityAndBlockEntityTick; -+ public Runnable tickStep_tickDragonFight; -+ public Runnable tickStep_activateEntities; -+ public Runnable tickStep_tickEntityList; -+ public Runnable tickStep_tickBlockEntities; -+ -+ private long tick_scheduledBlocksGameTime; -+ private boolean tick_isDebug; -+ private boolean tick_doEntityAndBlockEntityTick; -+ // Gale end - split tick steps -+ - public void tick(BooleanSupplier shouldKeepTicking) { -+ // Gale start - split tick steps -+ this.initializeTickSteps(); -+ this.tickStep_updatePlayersAffectingSpawning.run(); -+ this.tickStep_startHandlingTick.run(); -+ this.tickStep_tickWorldBorder.run(); -+ this.tickStep_advanceWeatherCycle.run(); -+ this.tickStep_applySleep.run(); -+ this.tickStep_updateSkyBrightness.run(); -+ this.tickStep_tickTime.run(); -+ this.tickStep_setScheduledBlocksGameTime.run(); -+ this.tickStep_setIsDebug.run(); -+ this.tickStep_tickBlocks.run(); -+ this.tickStep_tickFluids.run(); -+ this.tickStep_tickRaids.run(); -+ this.tickStep_tickChunkSource.run(); -+ this.tickStep_doRunBlockEvents.run(); -+ this.tickStep_stopHandlingTick.run(); -+ this.tickStep_setDoEntityAndBlockEntityTick.run(); -+ this.tickStep_tickDragonFight.run(); -+ this.tickStep_activateEntities.run(); -+ this.tickStep_tickEntityList.run(); -+ this.tickStep_tickBlockEntities.run(); -+ //this.entityManager.tick(); // Paper - rewrite chunk system -+ } -+ -+ public void initializeTickSteps() { -+ //noinspection ConstantValue -+ if (this.tickStep_updatePlayersAffectingSpawning != null) { -+ return; -+ } -+ this.tickStep_updatePlayersAffectingSpawning = this.tickStep_updatePlayersAffectingSpawning_create(); -+ this.tickStep_startHandlingTick = this.tickStep_startHandlingTick_create(); -+ this.tickStep_tickWorldBorder = this.tickStep_tickWorldBorder_create(); -+ this.tickStep_advanceWeatherCycle = this.tickStep_advanceWeatherCycle_create(); -+ this.tickStep_applySleep = this.tickStep_applySleep_create(); -+ this.tickStep_updateSkyBrightness = this.tickStep_updateSkyBrightness_create(); -+ this.tickStep_tickTime = this.tickStep_tickTime_create(); -+ this.tickStep_setScheduledBlocksGameTime = this.tickStep_setScheduledBlocksGameTime_create(); -+ this.tickStep_setIsDebug = this.tickStep_setIsDebug_create(); -+ this.tickStep_tickBlocks = this.tickStep_tickBlocks_create(); -+ this.tickStep_tickFluids = this.tickStep_tickFluids_create(); -+ this.tickStep_tickRaids = this.tickStep_tickRaids_create(); -+ this.tickStep_tickChunkSource = this.tickStep_tickChunkSource_create(); -+ this.tickStep_doRunBlockEvents = this.tickStep_doRunBlockEvents_create(); -+ this.tickStep_stopHandlingTick = this.tickStep_stopHandlingTick_create(); -+ this.tickStep_setDoEntityAndBlockEntityTick = this.tickStep_setDoEntityAndBlockEntityTick_create(); -+ this.tickStep_tickDragonFight = this.tickStep_tickDragonFight_create(); -+ this.tickStep_activateEntities = this.tickStep_activateEntities_create(); -+ this.tickStep_tickEntityList = this.tickStep_tickEntityList_create(); -+ this.tickStep_tickBlockEntities = this.tickStep_tickBlockEntities_create(); -+ } -+ -+ public Runnable wrapTickStep(Runnable runnable) { -+ return () -> { -+ try { -+ this.timings.doTick.startTiming(); // Spigot -+ runnable.run(); -+ this.timings.doTick.stopTiming(); // Spigot -+ } catch (Throwable throwable) { -+ MinecraftServer.SERVER.catchWorldTickException(this, throwable); -+ } -+ }; -+ } -+ -+ private Runnable tickStep_updatePlayersAffectingSpawning_create() { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - // Paper start - optimise checkDespawn - this.playersAffectingSpawning.clear(); - for (ServerPlayer player : this.players) { -@@ -641,10 +732,35 @@ public class ServerLevel extends Level implements WorldGenLevel { - this.playersAffectingSpawning.add(player); - } - } -- // Paper end - optimise checkDespawn -- this.handlingTick = true; -+ // Gale start - split tick steps -+ }); -+ } -+ -+ private Runnable tickStep_startHandlingTick_create() { -+ return this.wrapTickStep(() -> { -+ this.handlingTick = true; -+ }); -+ } -+ -+ private Runnable tickStep_tickWorldBorder_create() { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - this.getWorldBorder().tick(); -+ // Gale start - split tick steps -+ }); -+ } -+ -+ private Runnable tickStep_advanceWeatherCycle_create() { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - this.advanceWeatherCycle(); -+ // Gale start - split tick steps -+ }); -+ } -+ -+ private Runnable tickStep_applySleep_create() { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); - long j; - -@@ -667,40 +783,155 @@ public class ServerLevel extends Level implements WorldGenLevel { - this.resetWeatherCycle(); - } - } -+ // Gale start - split tick steps -+ }); -+ } - -+ private Runnable tickStep_updateSkyBrightness_create() { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - this.updateSkyBrightness(); -+ // Gale start - split tick steps -+ }); -+ } -+ -+ private Runnable tickStep_tickTime_create() { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - this.tickTime(); -+ // Gale start - split tick steps -+ }); -+ } -+ -+ private Runnable wrapScheduledBlocksTickStep(Runnable runnable) { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - timings.scheduledBlocks.startTiming(); // Paper -- if (!this.isDebug()) { -- j = this.getGameTime(); -- this.blockTicks.tick(j, 65536, this::tickBlock); -- this.fluidTicks.tick(j, 65536, this::tickFluid); -- } -+ // Gale start - split tick steps -+ runnable.run(); - timings.scheduledBlocks.stopTiming(); // Paper -+ }); -+ } -+ -+ private Runnable tickStep_setScheduledBlocksGameTime_create() { -+ return this.wrapScheduledBlocksTickStep(() -> { -+ this.tick_scheduledBlocksGameTime = this.getGameTime(); -+ }); -+ } -+ -+ private Runnable tickStep_setIsDebug_create() { -+ return this.wrapScheduledBlocksTickStep(() -> { -+ this.tick_isDebug = this.isDebug(); -+ }); -+ } -+ -+ private Runnable wrapNonDebugScheduledBlocksTickStep(Runnable runnable) { -+ return this.wrapScheduledBlocksTickStep(() -> { -+ if (!this.tick_isDebug) { -+ runnable.run(); -+ } -+ }); -+ } -+ -+ private Runnable tickStep_tickBlocks_create() { -+ return this.wrapScheduledBlocksTickStep(() -> { -+ this.blockTicks.tick(this.tick_scheduledBlocksGameTime, 65536, this::tickBlock); -+ }); -+ } - -+ private Runnable tickStep_tickFluids_create() { -+ return this.wrapScheduledBlocksTickStep(() -> { -+ this.fluidTicks.tick(this.tick_scheduledBlocksGameTime, 65536, this::tickFluid); -+ }); -+ } -+ -+ private Runnable tickStep_tickRaids_create() { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - this.timings.raids.startTiming(); // Paper - timings - this.raids.tick(); - this.timings.raids.stopTiming(); // Paper - timings -- this.timings.chunkProviderTick.startTiming(); // Paper - timings -- this.getChunkSource().tick(shouldKeepTicking, true); -- this.timings.chunkProviderTick.stopTiming(); // Paper - timings -+ // Gale start - split tick steps -+ }); -+ } -+ -+ private Runnable tickStep_tickChunkSource_create() { -+ return () -> { -+ this.getChunkSource().tick(MinecraftServer.tick_shouldKeepTicking, true); -+ }; -+ } -+ -+ private Runnable tickStep_doRunBlockEvents_create() { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - timings.doSounds.startTiming(); // Spigot - this.runBlockEvents(); - timings.doSounds.stopTiming(); // Spigot -+ // Gale start - split tick steps -+ }); -+ } -+ -+ private Runnable tickStep_stopHandlingTick_create() { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - this.handlingTick = false; -+ // Gale start - split tick steps -+ }); -+ } -+ -+ private Runnable tickStep_setDoEntityAndBlockEntityTick_create() { -+ return this.wrapTickStep(() -> { -+ // Gale end - split tick steps - boolean flag = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players - - if (flag) { - this.resetEmptyTime(); - } - -- if (flag || this.emptyTime++ < 300) { -+ // Gale start - split tick steps -+ this.tick_doEntityAndBlockEntityTick = flag || this.emptyTime++ < 300; -+ }); -+ } -+ -+ private Runnable wrapDoEntityAndBlockEntityTickTickStep(Runnable runnable) { -+ return this.wrapTickStep(() -> { -+ if (this.tick_doEntityAndBlockEntityTick) { -+ runnable.run(); -+ } -+ }); -+ } -+ -+ private Runnable wrapTickEntitiesTickStep(Runnable runnable) { -+ return this.wrapDoEntityAndBlockEntityTickTickStep(() -> { -+ // Gale end - split tick steps - timings.tickEntities.startTiming(); // Spigot -+ // Gale start - split tick steps -+ runnable.run(); -+ timings.tickEntities.stopTiming(); // Spigot -+ }); -+ } -+ -+ private Runnable tickStep_tickDragonFight_create() { -+ return this.wrapTickEntitiesTickStep(() -> { -+ // Gale end - split tick steps - if (this.dragonFight != null) { - this.dragonFight.tick(); - } -+ // Gale start - split tick steps -+ }); -+ } - -+ private Runnable tickStep_activateEntities_create() { -+ return this.wrapTickEntitiesTickStep(() -> { -+ // Gale end - split tick steps - org.spigotmc.ActivationRange.activateEntities(this); // Spigot -+ // Gale start - split tick steps -+ }); -+ } -+ -+ private Runnable tickStep_tickEntityList_create() { -+ return this.wrapTickEntitiesTickStep(() -> { -+ // Gale end - split tick steps - timings.entityTick.startTiming(); // Spigot - this.entityTickList.forEach((entity) -> { - if (!entity.isRemoved()) { -@@ -738,11 +969,15 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - }); - timings.entityTick.stopTiming(); // Spigot -- timings.tickEntities.stopTiming(); // Spigot -- this.tickBlockEntities(); -- } -+ // Gale start - split tick steps -+ }); -+ } - -- //this.entityManager.tick(); // Paper - rewrite chunk system -+ private Runnable tickStep_tickBlockEntities_create() { -+ return this.wrapDoEntityAndBlockEntityTickTickStep(() -> { -+ // Gale end - split tick steps -+ this.tickBlockEntities(); -+ }); // Gale - split tick steps - } - - @Override diff --git a/patches/server/0167-Multithreaded-ticking.patch b/patches/server/0167-Multithreaded-ticking.patch deleted file mode 100644 index 806c1e2..0000000 --- a/patches/server/0167-Multithreaded-ticking.patch +++ /dev/null @@ -1,323 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Tue, 31 Jan 2023 22:39:10 +0100 -Subject: [PATCH] Multithreaded ticking - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java -index 2b723eaf400b102d3b21151f532cca1b45e17951..9e73ed7356a800f1173efd271e8714e667efc33c 100644 ---- a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java -+++ b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java -@@ -432,4 +432,39 @@ public class GaleGlobalConfiguration extends ConfigurationPart { - - } - -+ // Gale start - multithreading -+ public Multithreading multithreading; -+ public class Multithreading extends ConfigurationPart { -+ -+ // Gale start - multithreaded ticking -+ /** -+ * If multithreading is enabled for any ticking steps, the order of the individual ticking steps for which -+ * multithreading is disabled may still be different from vanilla. -+ *
    -+ * For example, instead of the following vanilla sequence: -+ *
      -+ *
    • Tick the entities in world A
    • -+ *
    • Tick the block entities in world A
    • -+ *
    • Tick the entities in world B
    • -+ * Tick the block entities in world B -+ *
    -+ * the following sequence may happen: -+ *
      -+ *
    • Tick the entities in world A
    • -+ *
    • Tick the entities in world B
    • -+ *
    • Tick the block entities in world A
    • -+ *
    • Tick the block entities in world B
    • -+ *
    -+ */ -+ public Ticking ticking; -+ public class Ticking extends ConfigurationPart { -+ -+ public boolean clearExplosionDensityCache = true; -+ -+ } -+ // Gale end - multithreaded ticking -+ -+ } -+ // Gale end - multithreading -+ - } -diff --git a/src/main/java/org/galemc/gale/tick/step/ServerChildrenTickSteps.java b/src/main/java/org/galemc/gale/tick/step/ServerChildrenTickSteps.java -new file mode 100644 -index 0000000000000000000000000000000000000000..9408c7c6e6b60bd9d5153912a2d85585bc7744d1 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/tick/step/ServerChildrenTickSteps.java -@@ -0,0 +1,146 @@ -+// Gale - multithreaded ticking -+ -+package org.galemc.gale.tick.step; -+ -+import net.minecraft.server.MinecraftServer; -+import org.galemc.gale.configuration.GaleGlobalConfiguration; -+ -+/** -+ * A utility class defining the tick steps in {@link MinecraftServer#tickChildren} in a way -+ * that they can be executed both in the default order, or submitted as tasks that may run in parallel. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public final class ServerChildrenTickSteps { -+ -+ private ServerChildrenTickSteps() {} -+ -+ public static final ServerTickStep doSchedulerHeartbeat = -+ new ServerTickStep("doSchedulerHeartbeat", () -> MinecraftServer.SERVER.tickStep_doSchedulerHeartbeat()); -+ public static final ServerTickStep tickFunctions = -+ new ServerTickStep("tickFunctions", () -> MinecraftServer.SERVER.tickStep_tickFunctions()); -+ public static final ServerTickStep runProcessQueueTasks = -+ new ServerTickStep("runProcessQueueTasks", () -> MinecraftServer.SERVER.tickStep_runProcessQueueTasks()); -+ public static final WorldTickStep sendTimeUpdates = -+ new WorldTickStep("sendTimeUpdates", world -> MinecraftServer.SERVER.tickStep_sendTimeUpdates(world)); -+ public static final ServerTickStep startIteratingOverLevels = -+ new ServerTickStep("startIteratingOverLevels", () -> MinecraftServer.SERVER.tickStep_startIteratingOverLevels()); -+ public static final WorldTickStep updateEvents = -+ new WorldTickStep("updateEvents", world -> MinecraftServer.SERVER.tickStep_updateEvents(world)); -+ -+ public static final WorldTickStep updatePlayersAffectingSpawning = -+ new WorldTickStep("updatePlayersAffectingSpawning", world -> world.tickStep_updatePlayersAffectingSpawning.run()); -+ public static final WorldTickStep startHandlingTick = -+ new WorldTickStep("startHandlingTick", world -> world.tickStep_startHandlingTick.run()); -+ public static final WorldTickStep tickWorldBorder = -+ new WorldTickStep("tickWorldBorder", world -> world.tickStep_tickWorldBorder.run()); -+ public static final WorldTickStep advanceWeatherCycle = -+ new WorldTickStep("advanceWeatherCycle", world -> world.tickStep_advanceWeatherCycle.run()); -+ public static final WorldTickStep applySleep = -+ new WorldTickStep("applySleep", world -> world.tickStep_applySleep.run()); -+ public static final WorldTickStep updateSkyBrightness = -+ new WorldTickStep("updateSkyBrightness", world -> world.tickStep_updateSkyBrightness.run()); -+ public static final WorldTickStep tickTime = -+ new WorldTickStep("tickTime", world -> world.tickStep_tickTime.run()); -+ public static final WorldTickStep setScheduledBlocksGameTime = -+ new WorldTickStep("setScheduledBlocksGameTime", world -> world.tickStep_setScheduledBlocksGameTime.run()); -+ public static final WorldTickStep setIsDebug = -+ new WorldTickStep("setIsDebug", world -> world.tickStep_setIsDebug.run()); -+ public static final WorldTickStep tickBlocks = -+ new WorldTickStep("tickBlocks", world -> world.tickStep_tickBlocks.run()); -+ public static final WorldTickStep tickFluids = -+ new WorldTickStep("tickFluids", world -> world.tickStep_tickFluids.run()); -+ public static final WorldTickStep tickRaids = -+ new WorldTickStep("tickRaids", world -> world.tickStep_tickRaids.run()); -+ -+ public static final WorldTickStep purgeStaleTickets = -+ new WorldTickStep("purgeStaleTickets", world -> world.getChunkSource().tickStep_purgeStaleTickets.run()); -+ public static final WorldTickStep runDistanceManagerUpdates = -+ new WorldTickStep("runDistanceManagerUpdates", world -> world.getChunkSource().tickStep_runDistanceManagerUpdates.run()); -+ public static final WorldTickStep tickChunks = -+ new WorldTickStep("tickChunks", world -> world.getChunkSource().tickStep_tickChunks.run()); -+ public static final WorldTickStep tickChunkMap = -+ new WorldTickStep("tickChunkMap", world -> world.getChunkSource().tickStep_tickChunkMap.run()); -+ public static final WorldTickStep clearCache = -+ new WorldTickStep("clearCache", world -> world.getChunkSource().tickStep_clearCache.run()); -+ -+ public static final WorldTickStep doRunBlockEvents = -+ new WorldTickStep("doRunBlockEvents", world -> world.tickStep_doRunBlockEvents.run()); -+ public static final WorldTickStep stopHandlingTick = -+ new WorldTickStep("stopHandlingTick", world -> world.tickStep_stopHandlingTick.run()); -+ public static final WorldTickStep setDoEntityAndBlockEntityTick = -+ new WorldTickStep("setDoEntityAndBlockEntityTick", world -> world.tickStep_setDoEntityAndBlockEntityTick.run()); -+ public static final WorldTickStep tickDragonFight = -+ new WorldTickStep("tickDragonFight", world -> world.tickStep_tickDragonFight.run()); -+ public static final WorldTickStep activateEntities = -+ new WorldTickStep("activateEntities", world -> world.tickStep_activateEntities.run()); -+ public static final WorldTickStep tickEntityList = -+ new WorldTickStep("tickEntityList", world -> world.tickStep_tickEntityList.run()); -+ public static final WorldTickStep tickBlockEntities = -+ new WorldTickStep("tickBlockEntities", world -> world.tickStep_tickBlockEntities.run()); -+ -+ public static final WorldTickStep recalculateRegions = -+ new WorldTickStep("recalculateRegions", world -> MinecraftServer.SERVER.tickStep_recalculateRegions(world)); -+ public static final WorldTickStep clearExplosionDensityCache = (WorldTickStep) -+ new WorldTickStep("clearExplosionDensityCache", world -> MinecraftServer.SERVER.tickStep_clearExplosionDensityCache(world)) -+ .withGetDoMultithreaded(() -> GaleGlobalConfiguration.get().multithreading.ticking.clearExplosionDensityCache); -+ public static final ServerTickStep stopIteratingOverLevels = -+ new ServerTickStep("stopIteratingOverLevels", () -> MinecraftServer.SERVER.tickStep_stopIteratingOverLevels()); -+ public static final ServerTickStep tickConnection = -+ new ServerTickStep("tickConnection", () -> MinecraftServer.SERVER.tickStep_tickConnection()); -+ public static final ServerTickStep tickPlayerList = -+ new ServerTickStep("tickPlayerList", () -> MinecraftServer.SERVER.tickStep_tickPlayerList()); -+ public static final ServerTickStep tickGameTestTicker = -+ new ServerTickStep("tickGameTestTicker", () -> MinecraftServer.SERVER.tickStep_tickGameTestTicker()); -+ public static final ServerTickStep runTickables = -+ new ServerTickStep("runTickables", () -> MinecraftServer.SERVER.tickStep_runTickables()); -+ -+ /** -+ * The {@linkplain TickStep tick steps} that occur in {@link MinecraftServer#tickChildren}, -+ * in the same order. -+ */ -+ public static final TickStep[] values = { -+ doSchedulerHeartbeat, -+ tickFunctions, -+ runProcessQueueTasks, -+ sendTimeUpdates, -+ startIteratingOverLevels, -+ updateEvents, -+ -+ updatePlayersAffectingSpawning, -+ startHandlingTick, -+ tickWorldBorder, -+ advanceWeatherCycle, -+ applySleep, -+ updateSkyBrightness, -+ tickTime, -+ setScheduledBlocksGameTime, -+ setIsDebug, -+ tickBlocks, -+ tickFluids, -+ tickRaids, -+ -+ purgeStaleTickets, -+ runDistanceManagerUpdates, -+ tickChunks, -+ tickChunkMap, -+ clearCache, -+ -+ doRunBlockEvents, -+ stopHandlingTick, -+ setDoEntityAndBlockEntityTick, -+ tickDragonFight, -+ activateEntities, -+ tickEntityList, -+ tickBlockEntities, -+ -+ recalculateRegions, -+ clearExplosionDensityCache, -+ stopIteratingOverLevels, -+ tickConnection, -+ tickPlayerList, -+ tickGameTestTicker, -+ runTickables -+ }; -+ -+} -diff --git a/src/main/java/org/galemc/gale/tick/step/ServerTickStep.java b/src/main/java/org/galemc/gale/tick/step/ServerTickStep.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f6a4f0ef64cf39e69a55a231c8a1cecaf930a40a ---- /dev/null -+++ b/src/main/java/org/galemc/gale/tick/step/ServerTickStep.java -@@ -0,0 +1,22 @@ -+// Gale - multithreaded ticking -+ -+package org.galemc.gale.tick.step; -+ -+/** -+ * A {@link TickStep} that applies to the whole server (i.e. is run once per tick). -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public class ServerTickStep extends TickStep { -+ -+ /** -+ * A {@link Runnable} that performs this tick step. -+ */ -+ public final Runnable action; -+ -+ ServerTickStep(String name, Runnable action) { -+ super(name); -+ this.action = action; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/tick/step/TickStep.java b/src/main/java/org/galemc/gale/tick/step/TickStep.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d34aa02b1164f96bf614381aca756faba0f044b4 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/tick/step/TickStep.java -@@ -0,0 +1,53 @@ -+// Gale - multithreaded ticking -+ -+package org.galemc.gale.tick.step; -+ -+import java.util.function.Supplier; -+ -+/** -+ * An abstract step of ticking, as used in {@link ServerChildrenTickSteps}. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public abstract class TickStep { -+ -+ /** -+ * A human-readable name for this tick step, for use in debugging. -+ */ -+ public final String name; -+ -+ /** -+ * Whether to execute this tick step over multiple threads, as far as possible. -+ *
    -+ * This value is set by using {@link #updateDoMultithreaded()}. -+ */ -+ public boolean doMultithreaded; -+ -+ /** -+ * The supplier for updating {@link #doMultithreaded} via {@link #updateDoMultithreaded()}. -+ *
    -+ * This can be set via {@link #withGetDoMultithreaded} -+ */ -+ private Supplier getDoMultiThreaded = () -> false; -+ -+ protected TickStep(String name) { -+ this.name = name; -+ } -+ -+ /** -+ * @param getDoMultithreaded The new value for {@link #getDoMultiThreaded}. -+ * @return This instance. -+ */ -+ TickStep withGetDoMultithreaded(Supplier getDoMultithreaded) { -+ this.getDoMultiThreaded = getDoMultithreaded; -+ return this; -+ } -+ -+ /** -+ * Updates {@link #doMultithreaded} according to {@link #getDoMultiThreaded}. -+ */ -+ public void updateDoMultithreaded() { -+ this.doMultithreaded = this.getDoMultiThreaded.get(); -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/tick/step/WorldTickStep.java b/src/main/java/org/galemc/gale/tick/step/WorldTickStep.java -new file mode 100644 -index 0000000000000000000000000000000000000000..2c07a88ffb341ce57a030c5d72b55debbd34fec6 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/tick/step/WorldTickStep.java -@@ -0,0 +1,26 @@ -+// Gale - multithreaded ticking -+ -+package org.galemc.gale.tick.step; -+ -+import net.minecraft.server.level.ServerLevel; -+ -+import java.util.function.Consumer; -+ -+/** -+ * A {@link TickStep} that applies to each individual world separately. -+ * -+ * @author Martijn Muijsers under AGPL-3.0 -+ */ -+public class WorldTickStep extends TickStep { -+ -+ /** -+ * A {@link Consumer} that performs this tick step for a given {@link ServerLevel}. -+ */ -+ public final Consumer action; -+ -+ WorldTickStep(String name, Consumer action) { -+ super(name); -+ this.action = action; -+ } -+ -+} diff --git a/patches/server/0168-Yielding-ChunkTaskScheduler.patch b/patches/server/0168-Yielding-ChunkTaskScheduler.patch deleted file mode 100644 index efaa910..0000000 --- a/patches/server/0168-Yielding-ChunkTaskScheduler.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Martijn Muijsers -Date: Sun, 5 Feb 2023 19:04:45 +0100 -Subject: [PATCH] Yielding ChunkTaskScheduler - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) -Gale - https://galemc.org - -diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java -index 79ef41d2bb30beee2355d1de3dc99c9e00d929d5..0b41ffacba43ba784450ec52b0a6e96ac5bd864b 100644 ---- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java -+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java -@@ -22,6 +22,8 @@ import net.minecraft.world.level.chunk.ChunkAccess; - import net.minecraft.world.level.chunk.ChunkStatus; - import net.minecraft.world.level.chunk.LevelChunk; - import org.bukkit.Bukkit; -+import org.galemc.gale.executor.lock.MultipleWaitingBaseThreadsYieldingLock; -+import org.galemc.gale.executor.lock.YieldingLock; - import org.galemc.gale.executor.thread.pool.BaseThreadPool; - import org.slf4j.Logger; - import java.io.File; -@@ -116,7 +118,7 @@ public final class ChunkTaskScheduler { - - public final PrioritisedThreadedTaskQueue mainThreadExecutor = new PrioritisedThreadedTaskQueue(true); // Gale - base thread pool - private -> public, count delayed tasks - -- final ReentrantLock schedulingLock = new ReentrantLock(); -+ final YieldingLock schedulingLock = new MultipleWaitingBaseThreadsYieldingLock(new ReentrantLock()); // Gale - base thread pool - yielding ChunkTaskScheduler - public final ChunkHolderManager chunkHolderManager; - - static {