From c171237448de714f1679ca90e93fa34b2de7a65d Mon Sep 17 00:00:00 2001 From: MartijnMuijsers Date: Fri, 2 Dec 2022 10:08:07 +0100 Subject: [PATCH] Add base thread pool --- README.md | 11 +- ...Hide-irrelevant-compilation-warnings.patch | 3 +- patches/api/0002-Gale-configuration.patch | 10 +- patches/api/0003-SIMD-support.patch | 3 +- ...0004-Vectorized-map-color-conversion.patch | 3 +- .../0005-Skip-events-without-listeners.patch | 3 +- .../0006-Player-canSee-by-entity-UUID.patch | 27 +- .../api/0007-Specific-interval-TPS-API.patch | 3 +- patches/api/0008-5-second-TPS-average.patch | 27 +- patches/api/0009-Last-tick-time-API.patch | 13 +- patches/server/0001-Gale-project-setup.patch | 4 +- .../0002-Dev-import-deobfuscation-fixes.patch | 4 +- ...Hide-irrelevant-compilation-warnings.patch | 3 +- .../server/0004-Gale-branding-changes.patch | 4 +- patches/server/0005-Gale-configuration.patch | 10 +- .../0006-Set-Gale-permissions-root.patch | 4 +- patches/server/0007-Gale-commands.patch | 10 +- .../server/0008-Use-timin.gs-by-default.patch | 10 +- ...commend-disabling-timings-on-startup.patch | 3 +- ...cing-for-EntityLiving-hasLineOfSight.patch | 3 +- ...apelessRecipe-comparison-for-vanilla.patch | 3 +- ...0012-Reduce-projectile-chunk-loading.patch | 7 +- .../0013-Reduce-spooky-season-checks.patch | 3 +- .../server/0014-Move-random-tick-random.patch | 3 +- ...timize-random-calls-in-chunk-ticking.patch | 3 +- ...duce-enderman-teleport-chunk-lookups.patch | 3 +- ...educe-acquire-POI-for-stuck-entities.patch | 3 +- ...ve-iterators-from-Inventory-contains.patch | 3 +- ...ting-range-before-getting-visibility.patch | 3 +- ...e-for-plugins-not-shutting-down-task.patch | 3 +- ...0021-Improve-fluid-direction-caching.patch | 3 +- .../0022-Cache-on-climbable-check.patch | 3 +- ...3-Config-to-disable-vanilla-profiler.patch | 3 +- .../0024-Use-array-for-gamerule-storage.patch | 3 +- ...llisionContext-a-live-representation.patch | 3 +- ...ove-container-checking-with-a-bitset.patch | 3 +- ...er-checking-for-useless-move-packets.patch | 3 +- .../0028-Use-fast-item-merge-raytracing.patch | 3 +- ...e-aging-cache-for-biome-temperatures.patch | 3 +- patches/server/0030-Inline-level-height.patch | 3 +- ...-ThreadUnsafeRandom-for-mob-spawning.patch | 3 +- ...reams-and-iterators-from-range-check.patch | 3 +- ...-streams-from-getting-nearby-players.patch | 5 +- .../0034-Skip-cloning-loot-parameters.patch | 3 +- ...0035-Block-goal-does-not-load-chunks.patch | 3 +- .../0036-Reduce-entity-allocations.patch | 3 +- ...037-Remove-lambda-from-ticking-guard.patch | 3 +- ...ce-entity-fluid-lookups-if-no-fluids.patch | 3 +- patches/server/0039-SIMD-support.patch | 3 +- .../0040-Optimize-entity-coordinate-key.patch | 3 +- .../server/0041-Reduce-in-wall-checks.patch | 3 +- ...otable-refresh-for-non-player-intera.patch | 3 +- .../0043-Reduce-hopper-item-checks.patch | 3 +- .../0044-Reduce-villager-item-re-pickup.patch | 3 +- ...hat-commands-before-player-has-joine.patch | 3 +- .../0046-Do-not-log-invalid-statistics.patch | 3 +- ...47-Do-not-log-empty-message-warnings.patch | 3 +- ...0048-Do-not-log-ignored-advancements.patch | 27 +- ...49-Do-not-log-setBlock-in-far-chunks.patch | 27 +- ...0050-Do-not-log-unrecognized-recipes.patch | 27 +- ...t-log-legacy-Material-initialization.patch | 27 +- ...-Do-not-log-expired-message-warnings.patch | 3 +- .../0053-Do-not-log-Not-Secure-marker.patch | 3 +- .../0054-Do-not-log-run-as-root-warning.patch | 27 +- ...0055-Do-not-log-offline-mode-warning.patch | 27 +- ...ftly-log-invalid-pool-element-errors.patch | 3 +- .../0057-Fix-legacy-colors-in-console.patch | 27 +- ...ver-showing-in-ping-before-server-fu.patch | 27 +- ...59-Make-sand-duping-fix-configurable.patch | 27 +- patches/server/0060-Fix-MC-238526.patch | 27 +- patches/server/0061-Fix-MC-123848.patch | 27 +- ...cow-rotation-when-shearing-mooshroom.patch | 27 +- patches/server/0063-Fix-MC-121706.patch | 27 +- ...hould-check-if-entity-can-use-portal.patch | 27 +- ...-eating-blocks-in-non-ticking-chunks.patch | 27 +- ...ent-resetting-despawn-counter-config.patch | 27 +- .../0067-Reduce-array-allocations.patch | 3 +- .../server/0068-Optimize-sun-burn-tick.patch | 3 +- ...d-Optional-allocation-in-EntityBased.patch | 3 +- ...I-goal-set-with-optimized-collection.patch | 3 +- ...-rules-map-with-optimized-collection.patch | 3 +- ...ttributes-with-optimized-collections.patch | 3 +- ...-class-map-with-optimized-collection.patch | 3 +- ...hape-full-block-cache-with-hashtable.patch | 3 +- ...signableFrom-call-in-ClassInstanceMu.patch | 3 +- .../0076-Cache-BlockStatePairKey-hash.patch | 3 +- ...077-Cache-CubeVoxelShape-shape-array.patch | 3 +- ...-by-multiplication-in-CubePointRange.patch | 3 +- ...lace-parts-by-size-in-CubePointRange.patch | 3 +- ...ck-frozen-ticks-before-landing-block.patch | 3 +- .../0081-Faster-chunk-serialization.patch | 3 +- .../0082-Update-boss-bar-within-tick.patch | 3 +- .../0083-Cache-ominous-banner-item.patch | 3 +- .../0084-Precompute-piston-shapes.patch | 3 +- ...ld-generation-chunk-and-block-access.patch | 3 +- ...0086-Cache-world-generator-sea-level.patch | 3 +- ...Skip-entity-move-if-movement-is-zero.patch | 3 +- .../0088-Store-mob-counts-in-an-array.patch | 3 +- ...9-Use-linked-map-for-entity-trackers.patch | 3 +- .../0090-Optimize-noise-generation.patch | 3 +- ...-durability-change-equipment-updates.patch | 3 +- ...mes-on-entities-with-fire-resistance.patch | 3 +- ...93-Skip-cloning-advancement-criteria.patch | 3 +- .../0094-Player-canSee-by-entity-UUID.patch | 27 +- ...5-Spread-out-sending-all-player-info.patch | 27 +- ...-player-list-for-sending-player-info.patch | 27 +- ...ndSendEvent-if-there-are-no-listener.patch | 27 +- ...098-Send-multiple-keep-alive-packets.patch | 27 +- ...-random-strolling-into-non-ticking-c.patch | 3 +- .../0100-Specific-interval-TPS-API.patch | 3 +- .../server/0101-5-second-TPS-average.patch | 27 +- .../server/0102-Measure-last-tick-time.patch | 19 +- patches/server/0103-Last-tick-time-API.patch | 13 +- ...4-Show-last-tick-time-in-tps-command.patch | 13 +- ...ncrease-time-statistics-in-intervals.patch | 25 +- ...heck-has-physics-before-same-vehicle.patch | 3 +- ...gible-planar-movement-multiplication.patch | 3 +- ... 0108-Reduce-RandomSource-instances.patch} | 7 +- ...0108-Variable-main-thread-task-delay.patch | 1108 ---- .../0109-Thread-safety-annotations.patch | 535 ++ .../server/0110-CPU-cores-estimation.patch | 22 +- .../0111-Add-centralized-AsyncExecutor.patch | 116 - patches/server/0111-Mutex-utility.patch | 129 + ...12-Paired-lock-and-condition-utility.patch | 42 + .../0112-Remove-Paper-async-executor.patch | 68 - .../0113-Remove-Paper-cleaner-executor.patch | 27 - .../0113-Unterminable-executor-utility.patch | 64 + .../0114-FIFO-concurrent-queue-utility.patch | 147 + .../0114-Remove-background-executor.patch | 78 - patches/server/0115-Base-thread-pool.patch | 5455 +++++++++++++++++ .../0115-Remove-bootstrap-executor.patch | 27 - .../0116-Remove-world-upgrade-executors.patch | 39 - .../0117-Remove-tab-complete-executor.patch | 29 - .../0118-Remove-text-filter-executor.patch | 28 - 134 files changed, 7304 insertions(+), 1658 deletions(-) rename patches/server/{0109-Reduce-RandomSource-instances.patch => 0108-Reduce-RandomSource-instances.patch} (97%) delete mode 100644 patches/server/0108-Variable-main-thread-task-delay.patch create mode 100644 patches/server/0109-Thread-safety-annotations.patch delete mode 100644 patches/server/0111-Add-centralized-AsyncExecutor.patch create mode 100644 patches/server/0111-Mutex-utility.patch create mode 100644 patches/server/0112-Paired-lock-and-condition-utility.patch delete mode 100644 patches/server/0112-Remove-Paper-async-executor.patch delete mode 100644 patches/server/0113-Remove-Paper-cleaner-executor.patch create mode 100644 patches/server/0113-Unterminable-executor-utility.patch create mode 100644 patches/server/0114-FIFO-concurrent-queue-utility.patch delete mode 100644 patches/server/0114-Remove-background-executor.patch create mode 100644 patches/server/0115-Base-thread-pool.patch delete mode 100644 patches/server/0115-Remove-bootstrap-executor.patch delete mode 100644 patches/server/0116-Remove-world-upgrade-executors.patch delete mode 100644 patches/server/0117-Remove-tab-complete-executor.patch delete mode 100644 patches/server/0118-Remove-text-filter-executor.patch diff --git a/README.md b/README.md index 515ec2b..f6457d3 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ -Gale logo +Gale logo

Gale

A Minecraft server fork of Paper

-
Master branch: no known issues!
+
In active testing - reporting any issues you encounter is highly appreciated!
-[![Discord](https://img.shields.io/discord/1045402468416233592?color=5865F2&label=discord&style=for-the-badge)](https://discord.com/invite/dKp4wTDNrw) +[![Discord](https://img.shields.io/discord/1045402468416233592?color=5865F2&label=discord&style=for-the-badge)](https://discord.com/invite/gwezNT8c24)
## About -Gale is a fork of [Paper](https://github.com/PaperMC/Paper). It is intended to provide high performance, but we do not make any specific claims. You can see a comparison with other forks [here](https://github.com/GaleMC/Gale/wiki/Comparison-with-other-forks). +Gale is a fork of [Paper](https://github.com/PaperMC/Paper). It is intended to provide strong performance, but we do not make any specific claims. +The project is in open alpha. We do not recommend running it on your production server. ## Building @@ -31,4 +32,4 @@ We gratefully include various tested and carefully individually reviewed changes We would also like to thank our friends at [MultiPaper](https://github.com/MultiPaper/MultiPaper) and MCMT ([Fabric](https://github.com/himekifee/MCMTFabric), [Forge](https://github.com/jediminer543/JMT-MCMT)) for their shared knowledge and hard work towards a common goal. ## License -Paperweight files are licensed under MIT. Patches are licensed under GPL-3.0, unless indicated differently in their header. \ No newline at end of file +Paperweight files are licensed under MIT. Patches are licensed under GPL-3.0, unless indicated differently in their header. diff --git a/patches/api/0001-Hide-irrelevant-compilation-warnings.patch b/patches/api/0001-Hide-irrelevant-compilation-warnings.patch index 48e9628..c0946ed 100644 --- a/patches/api/0001-Hide-irrelevant-compilation-warnings.patch +++ b/patches/api/0001-Hide-irrelevant-compilation-warnings.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 01:15:55 +0100 Subject: [PATCH] Hide irrelevant compilation warnings 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 9f5694204091e23c4771657127a06f98e27ad8f1..fdb495c0e0f1ce702a590e6a7bcdcf40fd418d1c 100644 diff --git a/patches/api/0002-Gale-configuration.patch b/patches/api/0002-Gale-configuration.patch index 54ad5b3..be02ba5 100644 --- a/patches/api/0002-Gale-configuration.patch +++ b/patches/api/0002-Gale-configuration.patch @@ -1,8 +1,16 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 21:03:04 +0100 Subject: [PATCH] Gale configuration +License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org + +This patch is based on the following patch: +"Timings v2" +By: Aikar +As part of: Paper (https://github.com/PaperMC/Paper) +Licensed under: MIT (https://opensource.org/licenses/MIT) diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java index e43fef0152468944d8a33036344a43e95fe58476..fc0f70f7af8e12347ea67d9985d06c8323b2a3c0 100644 diff --git a/patches/api/0003-SIMD-support.patch b/patches/api/0003-SIMD-support.patch index 7976ff1..87f7f19 100644 --- a/patches/api/0003-SIMD-support.patch +++ b/patches/api/0003-SIMD-support.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 24 Nov 2022 01:21:32 +0100 Subject: [PATCH] SIMD support 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: "Add SIMD utilities" diff --git a/patches/api/0004-Vectorized-map-color-conversion.patch b/patches/api/0004-Vectorized-map-color-conversion.patch index a2aaa7a..03ab367 100644 --- a/patches/api/0004-Vectorized-map-color-conversion.patch +++ b/patches/api/0004-Vectorized-map-color-conversion.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 24 Nov 2022 17:03:21 +0100 Subject: [PATCH] Vectorized map color conversion 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: "Optimize map rendering" diff --git a/patches/api/0005-Skip-events-without-listeners.patch b/patches/api/0005-Skip-events-without-listeners.patch index d4e7e0d..f217db3 100644 --- a/patches/api/0005-Skip-events-without-listeners.patch +++ b/patches/api/0005-Skip-events-without-listeners.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 1 Dec 2022 13:45:13 +0100 Subject: [PATCH] Skip events without listeners 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 event if no listeners" diff --git a/patches/api/0006-Player-canSee-by-entity-UUID.patch b/patches/api/0006-Player-canSee-by-entity-UUID.patch index 997ed6f..7b708e6 100644 --- a/patches/api/0006-Player-canSee-by-entity-UUID.patch +++ b/patches/api/0006-Player-canSee-by-entity-UUID.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 23:38:12 +0100 Subject: [PATCH] Player#canSee by entity UUID License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Spread out and optimise player list ticksSpread out and optimise player list ticks" @@ -11,6 +12,30 @@ By: James Lyne As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java index 1e27b9de47f111b9c000243214e22890e323f7fc..9ce603a0c905017f512260de110cf7d8d2097028 100644 --- a/src/main/java/org/bukkit/entity/Player.java diff --git a/patches/api/0007-Specific-interval-TPS-API.patch b/patches/api/0007-Specific-interval-TPS-API.patch index 5aa9e21..1825330 100644 --- a/patches/api/0007-Specific-interval-TPS-API.patch +++ b/patches/api/0007-Specific-interval-TPS-API.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 12:57:37 +0100 Subject: [PATCH] Specific interval TPS API 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/Server.java b/src/main/java/org/bukkit/Server.java index fc0f70f7af8e12347ea67d9985d06c8323b2a3c0..f502bf2d511df86bc3c0b6a28682484e07a14120 100644 diff --git a/patches/api/0008-5-second-TPS-average.patch b/patches/api/0008-5-second-TPS-average.patch index 817b65d..ab82183 100644 --- a/patches/api/0008-5-second-TPS-average.patch +++ b/patches/api/0008-5-second-TPS-average.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 13:00:40 +0100 Subject: [PATCH] 5 second TPS average License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Add 5 second tps average in /tps" @@ -11,6 +12,30 @@ By: William Blake Galbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/Server.java b/src/main/java/org/bukkit/Server.java index f502bf2d511df86bc3c0b6a28682484e07a14120..ed787f736e36c08e10dfdca204011381123f1949 100644 --- a/src/main/java/org/bukkit/Server.java diff --git a/patches/api/0009-Last-tick-time-API.patch b/patches/api/0009-Last-tick-time-API.patch index 6cb7ed2..ece5adc 100644 --- a/patches/api/0009-Last-tick-time-API.patch +++ b/patches/api/0009-Last-tick-time-API.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Sat, 26 Nov 2022 20:49:15 +0100 Subject: [PATCH] Last tick time API License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Add getLastTickMs api" @@ -11,6 +12,16 @@ By: tr7zw As part of: YAPFA (https://github.com/tr7zw/YAPFA) Licensed under: MIT (https://opensource.org/licenses/MIT) +* YAPFA copyright * + +Copyright 2020 tr7zw + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +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/Server.java b/src/main/java/org/bukkit/Server.java index ed787f736e36c08e10dfdca204011381123f1949..f3eaf734ee6414d8809f8da30de417da6677982c 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 8c0e6c0..9db39fa 100644 --- a/patches/server/0001-Gale-project-setup.patch +++ b/patches/server/0001-Gale-project-setup.patch @@ -1,8 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 24 Nov 2022 21:26:57 +0100 Subject: [PATCH] Gale project setup +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 b0e4f11e8af4b909a56bb5576d05ef0537fb25f7..3103e811e1f41b782a5b95537b921c59cac2054f 100644 diff --git a/patches/server/0002-Dev-import-deobfuscation-fixes.patch b/patches/server/0002-Dev-import-deobfuscation-fixes.patch index 4c88d50..950d1c3 100644 --- a/patches/server/0002-Dev-import-deobfuscation-fixes.patch +++ b/patches/server/0002-Dev-import-deobfuscation-fixes.patch @@ -1,8 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 15:16:54 +0100 Subject: [PATCH] Dev import deobfuscation fixes +License: GPL-3.0 (https://www.gnu.org/licenses/gpl-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 92a60fc35145b7dd5fe17ea9b6823a919f483945..2393b6a5f3f12c2b17b172ee8ca42ead218e2a10 100644 diff --git a/patches/server/0003-Hide-irrelevant-compilation-warnings.patch b/patches/server/0003-Hide-irrelevant-compilation-warnings.patch index 767316e..9607c62 100644 --- a/patches/server/0003-Hide-irrelevant-compilation-warnings.patch +++ b/patches/server/0003-Hide-irrelevant-compilation-warnings.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 01:17:03 +0100 Subject: [PATCH] Hide irrelevant compilation warnings 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 3103e811e1f41b782a5b95537b921c59cac2054f..c2f60f3f638d8a94046d20ff1dfca316a3395861 100644 diff --git a/patches/server/0004-Gale-branding-changes.patch b/patches/server/0004-Gale-branding-changes.patch index 2d6e372..9fdd108 100644 --- a/patches/server/0004-Gale-branding-changes.patch +++ b/patches/server/0004-Gale-branding-changes.patch @@ -1,8 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 15:34:33 +0100 Subject: [PATCH] Gale branding changes +License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) +Gale - https://galemc.org diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java index 7b1843e16745ca8db2244e17490d291401f22679..9c883616224f368beac5bedb68f8ec9cf6dfbfb9 100644 diff --git a/patches/server/0005-Gale-configuration.patch b/patches/server/0005-Gale-configuration.patch index 582cdcc..c7bea82 100644 --- a/patches/server/0005-Gale-configuration.patch +++ b/patches/server/0005-Gale-configuration.patch @@ -1,8 +1,16 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 21:05:47 +0100 Subject: [PATCH] Gale configuration +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: +"Paper config files" +By: Jake Potrebic +As part of: Paper (https://github.com/PaperMC/Paper) +Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java index 06bff37e4c1fddd3be6343049a66787c63fb420c..ce257cf454300603d93ed68eb409461111bbac31 100644 diff --git a/patches/server/0006-Set-Gale-permissions-root.patch b/patches/server/0006-Set-Gale-permissions-root.patch index c985379..5d6fcb6 100644 --- a/patches/server/0006-Set-Gale-permissions-root.patch +++ b/patches/server/0006-Set-Gale-permissions-root.patch @@ -1,8 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Fri, 25 Nov 2022 18:42:45 +0100 Subject: [PATCH] Set Gale permissions root +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/util/permissions/CraftDefaultPermissions.java b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java index 8e06bc11fb28baee3407bbfe9d7b3689d6f85ff2..2e23147f807c6620b54d3047fe24a3847900712c 100644 diff --git a/patches/server/0007-Gale-commands.patch b/patches/server/0007-Gale-commands.patch index ccdd0cd..7d74046 100644 --- a/patches/server/0007-Gale-commands.patch +++ b/patches/server/0007-Gale-commands.patch @@ -1,8 +1,16 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Sat, 26 Nov 2022 10:47:56 +0100 Subject: [PATCH] Gale commands +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: +"Paper command" +By: Zach Brown +As part of: Paper (https://github.com/PaperMC/Paper) +Licensed under: MIT (https://opensource.org/licenses/MIT) diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java index 661794310eb10d4b9fe29c9445abd57826214d41..d2b14c65e6a925ba4a29d48bcedc0d9504092052 100644 diff --git a/patches/server/0008-Use-timin.gs-by-default.patch b/patches/server/0008-Use-timin.gs-by-default.patch index ea62343..a170834 100644 --- a/patches/server/0008-Use-timin.gs-by-default.patch +++ b/patches/server/0008-Use-timin.gs-by-default.patch @@ -1,8 +1,16 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 1 Dec 2022 17:26:18 +0100 Subject: [PATCH] Use timin.gs by default +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: +"Optimize default values for configs" +By: etil2jz <81570777+etil2jz@users.noreply.github.com> +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/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java index 63ec2ebb71aa0e0dbb64bbce7cd3c9494e9ce2e7..20bbb6473f7e8574bc52842e72c12ffb29c68d23 100644 diff --git a/patches/server/0009-Recommend-disabling-timings-on-startup.patch b/patches/server/0009-Recommend-disabling-timings-on-startup.patch index 2d4dc57..43c2b59 100644 --- a/patches/server/0009-Recommend-disabling-timings-on-startup.patch +++ b/patches/server/0009-Recommend-disabling-timings-on-startup.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 16:02:19 +0100 Subject: [PATCH] Recommend disabling timings on startup 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: "Disable Paper timings by default" diff --git a/patches/server/0010-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch b/patches/server/0010-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch index 5e442da..a05aa13 100644 --- a/patches/server/0010-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch +++ b/patches/server/0010-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 16:15:43 +0100 Subject: [PATCH] Strip raytracing for EntityLiving#hasLineOfSight 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: "Strip raytracing for EntityLiving#hasLineOfSight" diff --git a/patches/server/0011-Simpler-ShapelessRecipe-comparison-for-vanilla.patch b/patches/server/0011-Simpler-ShapelessRecipe-comparison-for-vanilla.patch index b710010..5dd7e08 100644 --- a/patches/server/0011-Simpler-ShapelessRecipe-comparison-for-vanilla.patch +++ b/patches/server/0011-Simpler-ShapelessRecipe-comparison-for-vanilla.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 16:22:47 +0100 Subject: [PATCH] Simpler ShapelessRecipe comparison for vanilla 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: "Simpler ShapelessRecipes comparison for Vanilla" diff --git a/patches/server/0012-Reduce-projectile-chunk-loading.patch b/patches/server/0012-Reduce-projectile-chunk-loading.patch index 6318fb3..44f5726 100644 --- a/patches/server/0012-Reduce-projectile-chunk-loading.patch +++ b/patches/server/0012-Reduce-projectile-chunk-loading.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 24 Nov 2022 12:00:55 +0100 Subject: [PATCH] Reduce projectile chunk loading 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: "Reduce projectile chunk loading" @@ -30,7 +31,7 @@ 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 713c11d6547cb02ac4b6a02aec07a8ba68019f3f..420ea4cca5a26c1da96585a215eb3ed1d3c5a10c 100644 +index 713c11d6547cb02ac4b6a02aec07a8ba68019f3f..ea23e771ab2b77e8001d0eaaf834423353ef70c2 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java @@ -42,6 +42,44 @@ public abstract class Projectile extends Entity { @@ -52,7 +53,7 @@ index 713c11d6547cb02ac4b6a02aec07a8ba68019f3f..420ea4cca5a26c1da96585a215eb3ed1 + 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()).getChunkAtIfLoadedImmediately(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; + if (maxChunkLoadsPerTick >= 0 && chunksLoadedThisTick > maxChunkLoadsPerTick) { diff --git a/patches/server/0013-Reduce-spooky-season-checks.patch b/patches/server/0013-Reduce-spooky-season-checks.patch index ff2baf0..3c3fd31 100644 --- a/patches/server/0013-Reduce-spooky-season-checks.patch +++ b/patches/server/0013-Reduce-spooky-season-checks.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 16:29:01 +0100 Subject: [PATCH] Reduce spooky season checks 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: "Only check for spooky season once an hour" diff --git a/patches/server/0014-Move-random-tick-random.patch b/patches/server/0014-Move-random-tick-random.patch index d00469f..e8a6aa7 100644 --- a/patches/server/0014-Move-random-tick-random.patch +++ b/patches/server/0014-Move-random-tick-random.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 16:53:40 +0100 Subject: [PATCH] Move random tick random 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: "Move ThreadUnsafeRandom Initialization" diff --git a/patches/server/0015-Optimize-random-calls-in-chunk-ticking.patch b/patches/server/0015-Optimize-random-calls-in-chunk-ticking.patch index 9a90889..7ce3fd4 100644 --- a/patches/server/0015-Optimize-random-calls-in-chunk-ticking.patch +++ b/patches/server/0015-Optimize-random-calls-in-chunk-ticking.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 16:45:45 +0100 Subject: [PATCH] Optimize random calls in chunk ticking 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: "Optimize random calls in chunk ticking" diff --git a/patches/server/0016-Reduce-enderman-teleport-chunk-lookups.patch b/patches/server/0016-Reduce-enderman-teleport-chunk-lookups.patch index 7123f23..2278400 100644 --- a/patches/server/0016-Reduce-enderman-teleport-chunk-lookups.patch +++ b/patches/server/0016-Reduce-enderman-teleport-chunk-lookups.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 19:53:35 +0100 Subject: [PATCH] Reduce enderman teleport chunk lookups 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: "Reduce chunk loading & lookups" diff --git a/patches/server/0017-Reduce-acquire-POI-for-stuck-entities.patch b/patches/server/0017-Reduce-acquire-POI-for-stuck-entities.patch index d368cc8..f4b2f8a 100644 --- a/patches/server/0017-Reduce-acquire-POI-for-stuck-entities.patch +++ b/patches/server/0017-Reduce-acquire-POI-for-stuck-entities.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 20:12:48 +0100 Subject: [PATCH] Reduce acquire POI for stuck entities 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 POI finding if stuck in vehicle" diff --git a/patches/server/0018-Remove-iterators-from-Inventory-contains.patch b/patches/server/0018-Remove-iterators-from-Inventory-contains.patch index 3c89212..99b6d30 100644 --- a/patches/server/0018-Remove-iterators-from-Inventory-contains.patch +++ b/patches/server/0018-Remove-iterators-from-Inventory-contains.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 20:18:05 +0100 Subject: [PATCH] Remove iterators from Inventory#contains 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: "Remove iterators from inventory contains" diff --git a/patches/server/0019-Check-targeting-range-before-getting-visibility.patch b/patches/server/0019-Check-targeting-range-before-getting-visibility.patch index e445870..c256da5 100644 --- a/patches/server/0019-Check-targeting-range-before-getting-visibility.patch +++ b/patches/server/0019-Check-targeting-range-before-getting-visibility.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 20:21:06 +0100 Subject: [PATCH] Check targeting range before getting visibility 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: "Early return optimization for target finding" diff --git a/patches/server/0020-Print-stack-trace-for-plugins-not-shutting-down-task.patch b/patches/server/0020-Print-stack-trace-for-plugins-not-shutting-down-task.patch index 857c417..3fefb7e 100644 --- a/patches/server/0020-Print-stack-trace-for-plugins-not-shutting-down-task.patch +++ b/patches/server/0020-Print-stack-trace-for-plugins-not-shutting-down-task.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 20:28:07 +0100 Subject: [PATCH] Print stack trace for plugins not shutting down tasks 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: "More debug for plugins not shutting down tasks" diff --git a/patches/server/0021-Improve-fluid-direction-caching.patch b/patches/server/0021-Improve-fluid-direction-caching.patch index a83580e..3683164 100644 --- a/patches/server/0021-Improve-fluid-direction-caching.patch +++ b/patches/server/0021-Improve-fluid-direction-caching.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 20:35:40 +0100 Subject: [PATCH] Improve fluid direction caching 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: "Improve fluid direction caching" diff --git a/patches/server/0022-Cache-on-climbable-check.patch b/patches/server/0022-Cache-on-climbable-check.patch index b3ad9bb..62843c2 100644 --- a/patches/server/0022-Cache-on-climbable-check.patch +++ b/patches/server/0022-Cache-on-climbable-check.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 20:40:40 +0100 Subject: [PATCH] Cache on climbable check 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: "Cache climbing check for activation" diff --git a/patches/server/0023-Config-to-disable-vanilla-profiler.patch b/patches/server/0023-Config-to-disable-vanilla-profiler.patch index 091ff78..1c2ed33 100644 --- a/patches/server/0023-Config-to-disable-vanilla-profiler.patch +++ b/patches/server/0023-Config-to-disable-vanilla-profiler.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 21:45:42 +0100 Subject: [PATCH] Config to disable vanilla profiler 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: "Config to disable method profiler" diff --git a/patches/server/0024-Use-array-for-gamerule-storage.patch b/patches/server/0024-Use-array-for-gamerule-storage.patch index b2bf8e1..5f69fb9 100644 --- a/patches/server/0024-Use-array-for-gamerule-storage.patch +++ b/patches/server/0024-Use-array-for-gamerule-storage.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 21:52:25 +0100 Subject: [PATCH] Use array for gamerule storage 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: "Use array for gamerule storage" diff --git a/patches/server/0025-Make-EntityCollisionContext-a-live-representation.patch b/patches/server/0025-Make-EntityCollisionContext-a-live-representation.patch index 2ef318c..52a9490 100644 --- a/patches/server/0025-Make-EntityCollisionContext-a-live-representation.patch +++ b/patches/server/0025-Make-EntityCollisionContext-a-live-representation.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 22:03:33 +0100 Subject: [PATCH] Make EntityCollisionContext a live representation 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: "Make EntityCollisionContext a live representation" diff --git a/patches/server/0026-Improve-container-checking-with-a-bitset.patch b/patches/server/0026-Improve-container-checking-with-a-bitset.patch index 0f0c621..a660968 100644 --- a/patches/server/0026-Improve-container-checking-with-a-bitset.patch +++ b/patches/server/0026-Improve-container-checking-with-a-bitset.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 22:11:06 +0100 Subject: [PATCH] Improve container checking with a bitset 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: "Improve container checking with a bitset" diff --git a/patches/server/0027-Better-checking-for-useless-move-packets.patch b/patches/server/0027-Better-checking-for-useless-move-packets.patch index f630698..cfd4513 100644 --- a/patches/server/0027-Better-checking-for-useless-move-packets.patch +++ b/patches/server/0027-Better-checking-for-useless-move-packets.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 22:26:37 +0100 Subject: [PATCH] Better checking for useless move packets 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: "Better checking for useless move packets" diff --git a/patches/server/0028-Use-fast-item-merge-raytracing.patch b/patches/server/0028-Use-fast-item-merge-raytracing.patch index 40c2219..0364bd7 100644 --- a/patches/server/0028-Use-fast-item-merge-raytracing.patch +++ b/patches/server/0028-Use-fast-item-merge-raytracing.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 22:29:05 +0100 Subject: [PATCH] Use fast item merge raytracing 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: "Patch Paper to use fast item merge raytracing" diff --git a/patches/server/0029-Use-aging-cache-for-biome-temperatures.patch b/patches/server/0029-Use-aging-cache-for-biome-temperatures.patch index 25843dc..d0faa86 100644 --- a/patches/server/0029-Use-aging-cache-for-biome-temperatures.patch +++ b/patches/server/0029-Use-aging-cache-for-biome-temperatures.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 22:33:06 +0100 Subject: [PATCH] Use aging cache for biome temperatures 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: "Use aging cache for biome temperatures" diff --git a/patches/server/0030-Inline-level-height.patch b/patches/server/0030-Inline-level-height.patch index 38c7469..cedd518 100644 --- a/patches/server/0030-Inline-level-height.patch +++ b/patches/server/0030-Inline-level-height.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 22:41:54 +0100 Subject: [PATCH] Inline level height 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: "Ensure level height is inlined" diff --git a/patches/server/0031-Use-ThreadUnsafeRandom-for-mob-spawning.patch b/patches/server/0031-Use-ThreadUnsafeRandom-for-mob-spawning.patch index a238a8d..f41cf2a 100644 --- a/patches/server/0031-Use-ThreadUnsafeRandom-for-mob-spawning.patch +++ b/patches/server/0031-Use-ThreadUnsafeRandom-for-mob-spawning.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 22:49:59 +0100 Subject: [PATCH] Use ThreadUnsafeRandom for mob spawning 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: "Use thread unsafe random for mob spawning" diff --git a/patches/server/0032-Remove-streams-and-iterators-from-range-check.patch b/patches/server/0032-Remove-streams-and-iterators-from-range-check.patch index f4de2a7..bf982e0 100644 --- a/patches/server/0032-Remove-streams-and-iterators-from-range-check.patch +++ b/patches/server/0032-Remove-streams-and-iterators-from-range-check.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 22:53:39 +0100 Subject: [PATCH] Remove streams and iterators from range check 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: "Remove streams and iterators from range check" diff --git a/patches/server/0033-Remove-streams-from-getting-nearby-players.patch b/patches/server/0033-Remove-streams-from-getting-nearby-players.patch index 1e57fd7..28a5a5d 100644 --- a/patches/server/0033-Remove-streams-from-getting-nearby-players.patch +++ b/patches/server/0033-Remove-streams-from-getting-nearby-players.patch @@ -1,8 +1,11 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 22:56:19 +0100 Subject: [PATCH] Remove streams from getting nearby players +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: "Remove streams from getting nearby players" By: Paul Sauve diff --git a/patches/server/0034-Skip-cloning-loot-parameters.patch b/patches/server/0034-Skip-cloning-loot-parameters.patch index 3f19454..96820b7 100644 --- a/patches/server/0034-Skip-cloning-loot-parameters.patch +++ b/patches/server/0034-Skip-cloning-loot-parameters.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +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" diff --git a/patches/server/0035-Block-goal-does-not-load-chunks.patch b/patches/server/0035-Block-goal-does-not-load-chunks.patch index 1fcfb54..bc50932 100644 --- a/patches/server/0035-Block-goal-does-not-load-chunks.patch +++ b/patches/server/0035-Block-goal-does-not-load-chunks.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 23:05:45 +0100 Subject: [PATCH] Block goal does not load chunks 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: "Fix Paper#6045, block goal shouldn't load chunks" diff --git a/patches/server/0036-Reduce-entity-allocations.patch b/patches/server/0036-Reduce-entity-allocations.patch index 85092b3..7848ac9 100644 --- a/patches/server/0036-Reduce-entity-allocations.patch +++ b/patches/server/0036-Reduce-entity-allocations.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 23:10:56 +0100 Subject: [PATCH] Reduce entity allocations 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: "Reduce entity allocations" diff --git a/patches/server/0037-Remove-lambda-from-ticking-guard.patch b/patches/server/0037-Remove-lambda-from-ticking-guard.patch index b7dbbbd..004e647 100644 --- a/patches/server/0037-Remove-lambda-from-ticking-guard.patch +++ b/patches/server/0037-Remove-lambda-from-ticking-guard.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 23:13:56 +0100 Subject: [PATCH] Remove lambda from ticking guard 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: "Remove lambda from ticking guard" diff --git a/patches/server/0038-Reduce-entity-fluid-lookups-if-no-fluids.patch b/patches/server/0038-Reduce-entity-fluid-lookups-if-no-fluids.patch index bb5d67a..76f76ec 100644 --- a/patches/server/0038-Reduce-entity-fluid-lookups-if-no-fluids.patch +++ b/patches/server/0038-Reduce-entity-fluid-lookups-if-no-fluids.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 23:19:12 +0100 Subject: [PATCH] Reduce entity fluid lookups if no fluids 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: "Reduce entity fluid lookups if no fluids" diff --git a/patches/server/0039-SIMD-support.patch b/patches/server/0039-SIMD-support.patch index 1397560..0532670 100644 --- a/patches/server/0039-SIMD-support.patch +++ b/patches/server/0039-SIMD-support.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 24 Nov 2022 01:19:12 +0100 Subject: [PATCH] SIMD support 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: "Add SIMD utilities" diff --git a/patches/server/0040-Optimize-entity-coordinate-key.patch b/patches/server/0040-Optimize-entity-coordinate-key.patch index acd6820..ecff096 100644 --- a/patches/server/0040-Optimize-entity-coordinate-key.patch +++ b/patches/server/0040-Optimize-entity-coordinate-key.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 23 Nov 2022 23:32:51 +0100 Subject: [PATCH] Optimize entity coordinate key 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: "Optimize entity coordinate key" diff --git a/patches/server/0041-Reduce-in-wall-checks.patch b/patches/server/0041-Reduce-in-wall-checks.patch index b1225fe..8307a3c 100644 --- a/patches/server/0041-Reduce-in-wall-checks.patch +++ b/patches/server/0041-Reduce-in-wall-checks.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 24 Nov 2022 10:31:38 +0100 Subject: [PATCH] Reduce in wall checks 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: "Optimize suffocation" diff --git a/patches/server/0042-Don-t-trigger-lootable-refresh-for-non-player-intera.patch b/patches/server/0042-Don-t-trigger-lootable-refresh-for-non-player-intera.patch index e8132e4..b6d36f9 100644 --- a/patches/server/0042-Don-t-trigger-lootable-refresh-for-non-player-intera.patch +++ b/patches/server/0042-Don-t-trigger-lootable-refresh-for-non-player-intera.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 24 Nov 2022 22:07:56 +0100 Subject: [PATCH] Don't trigger lootable refresh for non-player interaction License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Don't trigger Lootable Refresh for non player interaction" diff --git a/patches/server/0043-Reduce-hopper-item-checks.patch b/patches/server/0043-Reduce-hopper-item-checks.patch index 97ce052..4d747a9 100644 --- a/patches/server/0043-Reduce-hopper-item-checks.patch +++ b/patches/server/0043-Reduce-hopper-item-checks.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 24 Nov 2022 23:03:52 +0100 Subject: [PATCH] Reduce hopper item checks License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) +Gale - https://galemc.org This patch is based on the following patch: "Improve Hopper Performance" diff --git a/patches/server/0044-Reduce-villager-item-re-pickup.patch b/patches/server/0044-Reduce-villager-item-re-pickup.patch index ab0241a..fffecaf 100644 --- a/patches/server/0044-Reduce-villager-item-re-pickup.patch +++ b/patches/server/0044-Reduce-villager-item-re-pickup.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 24 Nov 2022 23:15:49 +0100 Subject: [PATCH] Reduce villager item re-pickup License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Slow down villager pickup of thrown items" diff --git a/patches/server/0045-Do-not-process-chat-commands-before-player-has-joine.patch b/patches/server/0045-Do-not-process-chat-commands-before-player-has-joine.patch index 7331278..23590c8 100644 --- a/patches/server/0045-Do-not-process-chat-commands-before-player-has-joine.patch +++ b/patches/server/0045-Do-not-process-chat-commands-before-player-has-joine.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 15:33:32 +0100 Subject: [PATCH] Do not process chat/commands before player has joined License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Do not process chat/commands before player has joined" diff --git a/patches/server/0046-Do-not-log-invalid-statistics.patch b/patches/server/0046-Do-not-log-invalid-statistics.patch index 9b35f17..a69cef7 100644 --- a/patches/server/0046-Do-not-log-invalid-statistics.patch +++ b/patches/server/0046-Do-not-log-invalid-statistics.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 24 Nov 2022 23:39:32 +0100 Subject: [PATCH] Do not log invalid statistics License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Ignore statistics warnings" diff --git a/patches/server/0047-Do-not-log-empty-message-warnings.patch b/patches/server/0047-Do-not-log-empty-message-warnings.patch index 4c096c4..2ed2622 100644 --- a/patches/server/0047-Do-not-log-empty-message-warnings.patch +++ b/patches/server/0047-Do-not-log-empty-message-warnings.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 24 Nov 2022 23:45:57 +0100 Subject: [PATCH] Do not log empty message warnings 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 0d72941a124a6fddad70362b25647743e601aafd..dd9d52552cc61729cdcd37caa87079e5026964d7 100644 diff --git a/patches/server/0048-Do-not-log-ignored-advancements.patch b/patches/server/0048-Do-not-log-ignored-advancements.patch index d7b7f60..ab2b394 100644 --- a/patches/server/0048-Do-not-log-ignored-advancements.patch +++ b/patches/server/0048-Do-not-log-ignored-advancements.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Fri, 25 Nov 2022 13:18:54 +0100 Subject: [PATCH] Do not log ignored advancements License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Logger settings (suppressing pointless logs)" @@ -11,6 +12,30 @@ By: William Blake Galbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java index a0c19503aabab5378d672a30163d35a5ba05b6c1..5ba0a756d45350dcda3caa5518c9a47a4de06f14 100644 --- a/src/main/java/net/minecraft/server/PlayerAdvancements.java diff --git a/patches/server/0049-Do-not-log-setBlock-in-far-chunks.patch b/patches/server/0049-Do-not-log-setBlock-in-far-chunks.patch index 0a086aa..700403c 100644 --- a/patches/server/0049-Do-not-log-setBlock-in-far-chunks.patch +++ b/patches/server/0049-Do-not-log-setBlock-in-far-chunks.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Fri, 25 Nov 2022 13:28:38 +0100 Subject: [PATCH] Do not log setBlock in far chunks License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Logger settings (suppressing pointless logs)" @@ -11,6 +12,30 @@ By: William Blake Galbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/server/level/WorldGenRegion.java b/src/main/java/net/minecraft/server/level/WorldGenRegion.java index 80c1e0e47818486a68e0114b063395290365346b..c9e719e676ea26ee94673538db2dfd9ce5c957ae 100644 --- a/src/main/java/net/minecraft/server/level/WorldGenRegion.java diff --git a/patches/server/0050-Do-not-log-unrecognized-recipes.patch b/patches/server/0050-Do-not-log-unrecognized-recipes.patch index 7abda7d..dfc4f18 100644 --- a/patches/server/0050-Do-not-log-unrecognized-recipes.patch +++ b/patches/server/0050-Do-not-log-unrecognized-recipes.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Fri, 25 Nov 2022 15:01:30 +0100 Subject: [PATCH] Do not log unrecognized recipes License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Logger settings (suppressing pointless logs)" @@ -11,6 +12,30 @@ By: William Blake Galbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/stats/ServerRecipeBook.java b/src/main/java/net/minecraft/stats/ServerRecipeBook.java index d13ed3069e944d138442ea440ac3eaf8d44c18d3..c89b8e5ea10a465160504f7364db47410ee5ec0d 100644 --- a/src/main/java/net/minecraft/stats/ServerRecipeBook.java diff --git a/patches/server/0051-Do-not-log-legacy-Material-initialization.patch b/patches/server/0051-Do-not-log-legacy-Material-initialization.patch index fec5f6f..84be0d1 100644 --- a/patches/server/0051-Do-not-log-legacy-Material-initialization.patch +++ b/patches/server/0051-Do-not-log-legacy-Material-initialization.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Fri, 25 Nov 2022 15:03:50 +0100 Subject: [PATCH] Do not log legacy Material initialization License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Logger settings (suppressing pointless logs)" @@ -11,6 +12,30 @@ By: William Blake Galbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/craftbukkit/legacy/CraftLegacy.java b/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java index b2a15c986c7500a0ce227a54cb61ec3f5378f6f3..3ea9b877a563d2b7894cf59799d654ebaa1f9819 100644 --- a/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java diff --git a/patches/server/0052-Do-not-log-expired-message-warnings.patch b/patches/server/0052-Do-not-log-expired-message-warnings.patch index 615a065..4a50be7 100644 --- a/patches/server/0052-Do-not-log-expired-message-warnings.patch +++ b/patches/server/0052-Do-not-log-expired-message-warnings.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Fri, 25 Nov 2022 15:07:55 +0100 Subject: [PATCH] Do not log expired message warnings 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 dd9d52552cc61729cdcd37caa87079e5026964d7..3c63449a36c6882b6e47f69f3cdf83e874a9a694 100644 diff --git a/patches/server/0053-Do-not-log-Not-Secure-marker.patch b/patches/server/0053-Do-not-log-Not-Secure-marker.patch index 2355d3d..7f4ee4c 100644 --- a/patches/server/0053-Do-not-log-Not-Secure-marker.patch +++ b/patches/server/0053-Do-not-log-Not-Secure-marker.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Fri, 25 Nov 2022 15:13:38 +0100 Subject: [PATCH] Do not log Not Secure marker 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 28ddab1121e314e82bf764fd89f36714bd7445f8..e54ffbacb5c2ae0235fe60d462cb6dbd49de0029 100644 diff --git a/patches/server/0054-Do-not-log-run-as-root-warning.patch b/patches/server/0054-Do-not-log-run-as-root-warning.patch index a0b5961..7c10c86 100644 --- a/patches/server/0054-Do-not-log-run-as-root-warning.patch +++ b/patches/server/0054-Do-not-log-run-as-root-warning.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 1 Dec 2022 16:43:50 +0100 Subject: [PATCH] Do not log run as root warning License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Options of warnings" @@ -15,6 +16,30 @@ Licensed under: MIT (https://opensource.org/licenses/MIT) Let users decide if we should warn while running in a root user +* KeYi copyright * + +MIT License + +Copyright (c) 2022 KeYiMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java index 007fe6820e208c04e81acbd7bb7eaf0a22f6f64f..8c7adc9edf941d5cc9aa2e427267eb035d4627d6 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java diff --git a/patches/server/0055-Do-not-log-offline-mode-warning.patch b/patches/server/0055-Do-not-log-offline-mode-warning.patch index 33520fa..0f9cc1d 100644 --- a/patches/server/0055-Do-not-log-offline-mode-warning.patch +++ b/patches/server/0055-Do-not-log-offline-mode-warning.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 1 Dec 2022 16:52:27 +0100 Subject: [PATCH] Do not log offline mode warning License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Options of warnings" @@ -15,6 +16,30 @@ Licensed under: MIT (https://opensource.org/licenses/MIT) Let users decide if we should warn while running in offline mode +* KeYi copyright * + +MIT License + +Copyright (c) 2022 KeYiMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java index 8c7adc9edf941d5cc9aa2e427267eb035d4627d6..8e45f712968303b7864d61adbf7325142c83f582 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java diff --git a/patches/server/0056-Softly-log-invalid-pool-element-errors.patch b/patches/server/0056-Softly-log-invalid-pool-element-errors.patch index 0875920..096fec6 100644 --- a/patches/server/0056-Softly-log-invalid-pool-element-errors.patch +++ b/patches/server/0056-Softly-log-invalid-pool-element-errors.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Fri, 25 Nov 2022 16:26:04 +0100 Subject: [PATCH] Softly log invalid pool element errors License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Change vanilla structure error to info log level" diff --git a/patches/server/0057-Fix-legacy-colors-in-console.patch b/patches/server/0057-Fix-legacy-colors-in-console.patch index 041ff85..ef82d3d 100644 --- a/patches/server/0057-Fix-legacy-colors-in-console.patch +++ b/patches/server/0057-Fix-legacy-colors-in-console.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 21:03:47 +0100 Subject: [PATCH] Fix legacy colors in console License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Fix legacy colors in console" @@ -11,6 +12,30 @@ By: BillyGalbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/io/papermc/paper/console/HexFormattingConverter.java b/src/main/java/io/papermc/paper/console/HexFormattingConverter.java index b4d0b7ecd56ab952319946854168c1299cb0b1be..beea5c17f07f85825ed1ba5a016f6edc93112115 100644 --- a/src/main/java/io/papermc/paper/console/HexFormattingConverter.java diff --git a/patches/server/0058-Fix-outdated-server-showing-in-ping-before-server-fu.patch b/patches/server/0058-Fix-outdated-server-showing-in-ping-before-server-fu.patch index b55d2f6..1038b5a 100644 --- a/patches/server/0058-Fix-outdated-server-showing-in-ping-before-server-fu.patch +++ b/patches/server/0058-Fix-outdated-server-showing-in-ping-before-server-fu.patch @@ -1,10 +1,11 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 15:50:22 +0100 Subject: [PATCH] Fix 'outdated server' showing in ping before server fully boots License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Fix 'outdated server' showing in ping before server fully boots" @@ -12,6 +13,30 @@ By: William Blake Galbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/server/network/ServerStatusPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java index f90eb6c50208879c4a2d5ad7e4181c06d38076f8..c374f2baff54b5ab1c8fdc1cedcecf2126da1783 100644 --- a/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java diff --git a/patches/server/0059-Make-sand-duping-fix-configurable.patch b/patches/server/0059-Make-sand-duping-fix-configurable.patch index 855e7dd..013c1c3 100644 --- a/patches/server/0059-Make-sand-duping-fix-configurable.patch +++ b/patches/server/0059-Make-sand-duping-fix-configurable.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 16:06:11 +0100 Subject: [PATCH] Make sand duping fix configurable License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Add toggle for sand duping fix" @@ -11,6 +12,30 @@ By: William Blake Galbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java index b2d1a17867cdbaad0c6e5c2376c716f9461af124..9d8a8dd2e6ec76beb6552266da7ae7c1229516fd 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java diff --git a/patches/server/0060-Fix-MC-238526.patch b/patches/server/0060-Fix-MC-238526.patch index 8d2c59b..2508f82 100644 --- a/patches/server/0060-Fix-MC-238526.patch +++ b/patches/server/0060-Fix-MC-238526.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 16:16:35 +0100 Subject: [PATCH] Fix MC-238526 License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "MC-238526 - Fix spawner not spawning water animals correctly" @@ -11,6 +12,30 @@ By: BillyGalbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/world/entity/animal/WaterAnimal.java b/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java index 18389f46902bb9879ac6d734723e9a720724dc48..613970368890d78ab589f654a66024eeef23983a 100644 --- a/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java diff --git a/patches/server/0061-Fix-MC-123848.patch b/patches/server/0061-Fix-MC-123848.patch index abfe30c..27d2c07 100644 --- a/patches/server/0061-Fix-MC-123848.patch +++ b/patches/server/0061-Fix-MC-123848.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 16:25:51 +0100 Subject: [PATCH] Fix MC-123848 License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Fix MC-123848" @@ -11,6 +12,30 @@ By: BillyGalbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/world/entity/decoration/ItemFrame.java b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java index b9cb39efe14a877ddedc5a5e4141ee5199ea8daf..bf2a4e129edf2c919c3e422756c67ea904b83b00 100644 --- a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java diff --git a/patches/server/0062-Fix-cow-rotation-when-shearing-mooshroom.patch b/patches/server/0062-Fix-cow-rotation-when-shearing-mooshroom.patch index ef54117..b2ab857 100644 --- a/patches/server/0062-Fix-cow-rotation-when-shearing-mooshroom.patch +++ b/patches/server/0062-Fix-cow-rotation-when-shearing-mooshroom.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 15:55:05 +0100 Subject: [PATCH] Fix cow rotation when shearing mooshroom License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Fix cow rotation when shearing mooshroom" @@ -11,6 +12,30 @@ By: William Blake Galbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java index c4441ef250b95663198cfc9607d714dbe346512c..81ca0f568e484b1d013a9abfa809f1ba9956933b 100644 --- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java diff --git a/patches/server/0063-Fix-MC-121706.patch b/patches/server/0063-Fix-MC-121706.patch index 8a9092b..544a502 100644 --- a/patches/server/0063-Fix-MC-121706.patch +++ b/patches/server/0063-Fix-MC-121706.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 16:42:48 +0100 Subject: [PATCH] Fix MC-121706 License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "MC-121706 - Fix mobs not looking up and down when strafing" @@ -11,6 +12,30 @@ By: Krakenied As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java index 6558b0d4bea99948fdc2b51751f3cfdc239d4b67..bedb5b8ee24817a494455f17e1f32b42d64c4dd2 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java diff --git a/patches/server/0064-End-gateway-should-check-if-entity-can-use-portal.patch b/patches/server/0064-End-gateway-should-check-if-entity-can-use-portal.patch index 30c1086..02c70f4 100644 --- a/patches/server/0064-End-gateway-should-check-if-entity-can-use-portal.patch +++ b/patches/server/0064-End-gateway-should-check-if-entity-can-use-portal.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 21:30:27 +0100 Subject: [PATCH] End gateway should check if entity can use portal License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "End gateway should check if entity can use portal" @@ -11,6 +12,30 @@ By: William Blake Galbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java index 89bf5fc4fab2cf39378600aa8925ac4bb7707428..8f3fb24afd3df8e9e4caa2917f598fcf74289726 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java diff --git a/patches/server/0065-Prevent-entities-eating-blocks-in-non-ticking-chunks.patch b/patches/server/0065-Prevent-entities-eating-blocks-in-non-ticking-chunks.patch index d10593f..bbe4b56 100644 --- a/patches/server/0065-Prevent-entities-eating-blocks-in-non-ticking-chunks.patch +++ b/patches/server/0065-Prevent-entities-eating-blocks-in-non-ticking-chunks.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 23:16:04 +0100 Subject: [PATCH] Prevent entities eating blocks in non-ticking chunks License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Dont eat blocks in non ticking chunks" @@ -11,6 +12,30 @@ By: BillyGalbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/world/entity/ai/goal/EatBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java index 80aa539f7c6a6ee44338de084cdcdf5fb4ef996a..cb8fc8a88c14d2374a0bbe35aa1c2056d625b71c 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java diff --git a/patches/server/0066-Make-arrow-movement-resetting-despawn-counter-config.patch b/patches/server/0066-Make-arrow-movement-resetting-despawn-counter-config.patch index 808b19a..16881af 100644 --- a/patches/server/0066-Make-arrow-movement-resetting-despawn-counter-config.patch +++ b/patches/server/0066-Make-arrow-movement-resetting-despawn-counter-config.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 21:37:22 +0100 Subject: [PATCH] Make arrow movement resetting despawn counter configurable License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Arrows should not reset despawn counter" @@ -16,6 +17,30 @@ Licensed under: MIT (https://opensource.org/licenses/MIT) This prevents keeping arrows alive indefinitely (such as when the block the arrow is stuck in gets removed, like a piston head going up/down) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/world/entity/projectile/AbstractArrow.java b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java index def3b3f8dac4b28eaaafdd9bc2e7f4b9ec9d6855..ca28f41a5a0405adf07cc2898d53a77f5c7b3e8c 100644 --- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java diff --git a/patches/server/0067-Reduce-array-allocations.patch b/patches/server/0067-Reduce-array-allocations.patch index 2ae37a8..b4a7240 100644 --- a/patches/server/0067-Reduce-array-allocations.patch +++ b/patches/server/0067-Reduce-array-allocations.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Sat, 26 Nov 2022 11:25:45 +0100 Subject: [PATCH] Reduce array allocations 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: "reduce allocs" diff --git a/patches/server/0068-Optimize-sun-burn-tick.patch b/patches/server/0068-Optimize-sun-burn-tick.patch index 170af4a..cd7290c 100644 --- a/patches/server/0068-Optimize-sun-burn-tick.patch +++ b/patches/server/0068-Optimize-sun-burn-tick.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 23:04:59 +0100 Subject: [PATCH] Optimize sun burn tick 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: "some entity micro opts" diff --git a/patches/server/0069-Reduce-lambda-and-Optional-allocation-in-EntityBased.patch b/patches/server/0069-Reduce-lambda-and-Optional-allocation-in-EntityBased.patch index 9dbcecd..51f4cdf 100644 --- a/patches/server/0069-Reduce-lambda-and-Optional-allocation-in-EntityBased.patch +++ b/patches/server/0069-Reduce-lambda-and-Optional-allocation-in-EntityBased.patch @@ -1,10 +1,11 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Sat, 26 Nov 2022 16:28:23 +0100 Subject: [PATCH] Reduce lambda and Optional allocation in EntityBasedExplosionDamageCalculator License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/alloc/explosion_behavior/EntityExplosionBehaviorMixin.java" diff --git a/patches/server/0070-Replace-AI-goal-set-with-optimized-collection.patch b/patches/server/0070-Replace-AI-goal-set-with-optimized-collection.patch index 7604d0b..7d75c9b 100644 --- a/patches/server/0070-Replace-AI-goal-set-with-optimized-collection.patch +++ b/patches/server/0070-Replace-AI-goal-set-with-optimized-collection.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 13:49:11 +0100 Subject: [PATCH] Replace AI goal set with optimized collection License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/collections/goals/GoalSelectorMixin.java" diff --git a/patches/server/0071-Replace-game-rules-map-with-optimized-collection.patch b/patches/server/0071-Replace-game-rules-map-with-optimized-collection.patch index f09a346..05debbb 100644 --- a/patches/server/0071-Replace-game-rules-map-with-optimized-collection.patch +++ b/patches/server/0071-Replace-game-rules-map-with-optimized-collection.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 17:35:06 +0100 Subject: [PATCH] Replace game rules map with optimized collection License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/collections/gamerules/GameRulesMixin.java" diff --git a/patches/server/0072-Replace-AI-attributes-with-optimized-collections.patch b/patches/server/0072-Replace-AI-attributes-with-optimized-collections.patch index d688930..03f4e98 100644 --- a/patches/server/0072-Replace-AI-attributes-with-optimized-collections.patch +++ b/patches/server/0072-Replace-AI-attributes-with-optimized-collections.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 20:54:31 +0100 Subject: [PATCH] Replace AI attributes with optimized collections License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/collections/attributes/AttributeContainerMixin.java" diff --git a/patches/server/0073-Replace-class-map-with-optimized-collection.patch b/patches/server/0073-Replace-class-map-with-optimized-collection.patch index f221baf..e4c1061 100644 --- a/patches/server/0073-Replace-class-map-with-optimized-collection.patch +++ b/patches/server/0073-Replace-class-map-with-optimized-collection.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 21:02:43 +0100 Subject: [PATCH] Replace class map with optimized collection License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/collections/entity_by_type/TypeFilterableListMixin.java" diff --git a/patches/server/0074-Replace-shape-full-block-cache-with-hashtable.patch b/patches/server/0074-Replace-shape-full-block-cache-with-hashtable.patch index c1f7be8..afb0bcf 100644 --- a/patches/server/0074-Replace-shape-full-block-cache-with-hashtable.patch +++ b/patches/server/0074-Replace-shape-full-block-cache-with-hashtable.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 1 Dec 2022 14:11:53 +0100 Subject: [PATCH] Replace shape full block cache with hashtable License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixins and classes: * "me/jellysquid/mods/lithium/common/util/collections/Object2BooleanCacheTable.java" diff --git a/patches/server/0075-Avoid-Class-isAssignableFrom-call-in-ClassInstanceMu.patch b/patches/server/0075-Avoid-Class-isAssignableFrom-call-in-ClassInstanceMu.patch index d97fb48..622ab93 100644 --- a/patches/server/0075-Avoid-Class-isAssignableFrom-call-in-ClassInstanceMu.patch +++ b/patches/server/0075-Avoid-Class-isAssignableFrom-call-in-ClassInstanceMu.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 21:15:33 +0100 Subject: [PATCH] Avoid Class#isAssignableFrom call in ClassInstanceMultiMap License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/collections/entity_filtering/TypeFilterableListMixin.java" diff --git a/patches/server/0076-Cache-BlockStatePairKey-hash.patch b/patches/server/0076-Cache-BlockStatePairKey-hash.patch index 1703992..28a2259 100644 --- a/patches/server/0076-Cache-BlockStatePairKey-hash.patch +++ b/patches/server/0076-Cache-BlockStatePairKey-hash.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 15:51:59 +0100 Subject: [PATCH] Cache BlockStatePairKey hash License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/cached_hashcode/BlockNeighborGroupMixin.java" diff --git a/patches/server/0077-Cache-CubeVoxelShape-shape-array.patch b/patches/server/0077-Cache-CubeVoxelShape-shape-array.patch index 1c9ab1f..2672bb1 100644 --- a/patches/server/0077-Cache-CubeVoxelShape-shape-array.patch +++ b/patches/server/0077-Cache-CubeVoxelShape-shape-array.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 17:19:01 +0100 Subject: [PATCH] Cache CubeVoxelShape shape array License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/shapes/precompute_shape_arrays/SimpleVoxelShapeMixin.java" diff --git a/patches/server/0078-Replace-division-by-multiplication-in-CubePointRange.patch b/patches/server/0078-Replace-division-by-multiplication-in-CubePointRange.patch index da1525a..1b370c1 100644 --- a/patches/server/0078-Replace-division-by-multiplication-in-CubePointRange.patch +++ b/patches/server/0078-Replace-division-by-multiplication-in-CubePointRange.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 17:24:24 +0100 Subject: [PATCH] Replace division by multiplication in CubePointRange License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/shapes/precompute_shape_arrays/FractionalDoubleListMixin.java" diff --git a/patches/server/0079-Replace-parts-by-size-in-CubePointRange.patch b/patches/server/0079-Replace-parts-by-size-in-CubePointRange.patch index ce42e71..7672615 100644 --- a/patches/server/0079-Replace-parts-by-size-in-CubePointRange.patch +++ b/patches/server/0079-Replace-parts-by-size-in-CubePointRange.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 17:28:37 +0100 Subject: [PATCH] Replace parts by size in CubePointRange License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org diff --git a/src/main/java/net/minecraft/world/phys/shapes/CubePointRange.java b/src/main/java/net/minecraft/world/phys/shapes/CubePointRange.java index e7bffb7f757dbeac255676823de6464329f2f535..55d4160b6bf9718d475e4bc7cecf048d62e00b82 100644 diff --git a/patches/server/0080-Check-frozen-ticks-before-landing-block.patch b/patches/server/0080-Check-frozen-ticks-before-landing-block.patch index 75e1565..acec81b 100644 --- a/patches/server/0080-Check-frozen-ticks-before-landing-block.patch +++ b/patches/server/0080-Check-frozen-ticks-before-landing-block.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 20:17:41 +0100 Subject: [PATCH] Check frozen ticks before landing block License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/entity/fast_powder_snow_check/LivingEntityMixin.java" diff --git a/patches/server/0081-Faster-chunk-serialization.patch b/patches/server/0081-Faster-chunk-serialization.patch index f56e713..0a06d4d 100644 --- a/patches/server/0081-Faster-chunk-serialization.patch +++ b/patches/server/0081-Faster-chunk-serialization.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 21:51:16 +0100 Subject: [PATCH] Faster chunk serialization License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixins and classes: * "me/jellysquid/mods/lithium/common/world/chunk/CompactingPackedIntegerArray.java" diff --git a/patches/server/0082-Update-boss-bar-within-tick.patch b/patches/server/0082-Update-boss-bar-within-tick.patch index 1ce6867..e708e88 100644 --- a/patches/server/0082-Update-boss-bar-within-tick.patch +++ b/patches/server/0082-Update-boss-bar-within-tick.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 1 Dec 2022 00:12:14 +0100 Subject: [PATCH] Update boss bar within tick License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/ai/raid/RaidMixin.java" diff --git a/patches/server/0083-Cache-ominous-banner-item.patch b/patches/server/0083-Cache-ominous-banner-item.patch index c937ac5..231fea6 100644 --- a/patches/server/0083-Cache-ominous-banner-item.patch +++ b/patches/server/0083-Cache-ominous-banner-item.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 1 Dec 2022 00:37:50 +0100 Subject: [PATCH] Cache ominous banner item License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/ai/raid/RaiderEntityMixin.java" diff --git a/patches/server/0084-Precompute-piston-shapes.patch b/patches/server/0084-Precompute-piston-shapes.patch index 35a40d1..0652dc8 100644 --- a/patches/server/0084-Precompute-piston-shapes.patch +++ b/patches/server/0084-Precompute-piston-shapes.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 1 Dec 2022 13:58:42 +0100 Subject: [PATCH] Precompute piston shapes License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixins and classes: * "me/jellysquid/mods/lithium/common/shapes/OffsetVoxelShapeCache.java" diff --git a/patches/server/0085-Optimize-world-generation-chunk-and-block-access.patch b/patches/server/0085-Optimize-world-generation-chunk-and-block-access.patch index ba4246b..80bee77 100644 --- a/patches/server/0085-Optimize-world-generation-chunk-and-block-access.patch +++ b/patches/server/0085-Optimize-world-generation-chunk-and-block-access.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 1 Dec 2022 14:33:06 +0100 Subject: [PATCH] Optimize world generation chunk and block access License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixins and classes: * "me/jellysquid/mods/lithium/common/util/Pos.java" diff --git a/patches/server/0086-Cache-world-generator-sea-level.patch b/patches/server/0086-Cache-world-generator-sea-level.patch index 3eccc20..02574ce 100644 --- a/patches/server/0086-Cache-world-generator-sea-level.patch +++ b/patches/server/0086-Cache-world-generator-sea-level.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 1 Dec 2022 14:18:46 +0100 Subject: [PATCH] Cache world generator sea level License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org This patch is based on the following mixin: "me/jellysquid/mods/lithium/mixin/gen/cached_generator_settings/NoiseChunkGeneratorMixin.java" diff --git a/patches/server/0087-Skip-entity-move-if-movement-is-zero.patch b/patches/server/0087-Skip-entity-move-if-movement-is-zero.patch index 8c0cc43..c2c5658 100644 --- a/patches/server/0087-Skip-entity-move-if-movement-is-zero.patch +++ b/patches/server/0087-Skip-entity-move-if-movement-is-zero.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 14:25:58 +0100 Subject: [PATCH] Skip entity move if movement is zero License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following mixin: "com/ishland/vmp/mixins/entity/move_zero_velocity/MixinEntity.java" diff --git a/patches/server/0088-Store-mob-counts-in-an-array.patch b/patches/server/0088-Store-mob-counts-in-an-array.patch index 7e47263..5bfc5b2 100644 --- a/patches/server/0088-Store-mob-counts-in-an-array.patch +++ b/patches/server/0088-Store-mob-counts-in-an-array.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 17:46:23 +0100 Subject: [PATCH] Store mob counts in an array License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following mixin: "com/ishland/vmp/mixins/general/spawn_density_cap/MixinSpawnDensityCapperDensityCap.java" diff --git a/patches/server/0089-Use-linked-map-for-entity-trackers.patch b/patches/server/0089-Use-linked-map-for-entity-trackers.patch index 52fe677..c0af40d 100644 --- a/patches/server/0089-Use-linked-map-for-entity-trackers.patch +++ b/patches/server/0089-Use-linked-map-for-entity-trackers.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Thu, 1 Dec 2022 14:40:03 +0100 Subject: [PATCH] Use linked map for entity trackers License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following mixin: "com/ishland/vmp/mixins/entitytracker/MixinThreadedAnvilChunkStorage.java" diff --git a/patches/server/0090-Optimize-noise-generation.patch b/patches/server/0090-Optimize-noise-generation.patch index d98e5b6..74b960d 100644 --- a/patches/server/0090-Optimize-noise-generation.patch +++ b/patches/server/0090-Optimize-noise-generation.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 19:49:14 +0100 Subject: [PATCH] Optimize noise generation License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following mixins: * "com/ishland/c2me/opts/math/mixin/MixinOctavePerlinNoiseSampler.java" diff --git a/patches/server/0091-Ignore-durability-change-equipment-updates.patch b/patches/server/0091-Ignore-durability-change-equipment-updates.patch index ce21d89..0299ff8 100644 --- a/patches/server/0091-Ignore-durability-change-equipment-updates.patch +++ b/patches/server/0091-Ignore-durability-change-equipment-updates.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +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" diff --git a/patches/server/0092-Hide-flames-on-entities-with-fire-resistance.patch b/patches/server/0092-Hide-flames-on-entities-with-fire-resistance.patch index 0066800..9d87715 100644 --- a/patches/server/0092-Hide-flames-on-entities-with-fire-resistance.patch +++ b/patches/server/0092-Hide-flames-on-entities-with-fire-resistance.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 14:39:27 +0100 Subject: [PATCH] Hide flames on entities with fire resistance License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Don't send fire packets if player has Fire Resistance" diff --git a/patches/server/0093-Skip-cloning-advancement-criteria.patch b/patches/server/0093-Skip-cloning-advancement-criteria.patch index b5c4103..eb8240e 100644 --- a/patches/server/0093-Skip-cloning-advancement-criteria.patch +++ b/patches/server/0093-Skip-cloning-advancement-criteria.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 16:54:28 +0100 Subject: [PATCH] Skip cloning advancement criteria 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" [sic] diff --git a/patches/server/0094-Player-canSee-by-entity-UUID.patch b/patches/server/0094-Player-canSee-by-entity-UUID.patch index 4e2a32a..6711ffb 100644 --- a/patches/server/0094-Player-canSee-by-entity-UUID.patch +++ b/patches/server/0094-Player-canSee-by-entity-UUID.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 23:37:12 +0100 Subject: [PATCH] Player#canSee by entity UUID License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Spread out and optimise player list ticksSpread out and optimise player list ticks" @@ -11,6 +12,30 @@ By: James Lyne As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index a5220a4e514865c70700527bcd2f941d852564c0..db83c62770d5bac7255251d26c75771ec6897317 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java diff --git a/patches/server/0095-Spread-out-sending-all-player-info.patch b/patches/server/0095-Spread-out-sending-all-player-info.patch index bb6d3a9..86c867c 100644 --- a/patches/server/0095-Spread-out-sending-all-player-info.patch +++ b/patches/server/0095-Spread-out-sending-all-player-info.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 23:18:57 +0100 Subject: [PATCH] Spread out sending all player info License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) +Gale - https://galemc.org This patch is based on the following patch: "Spread out and optimise player list ticksSpread out and optimise player list ticks" @@ -11,6 +12,30 @@ By: James Lyne As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java index 507017c1ea03cd028be2149b18c8de7f8353e37e..d2e6a92a44416bfaa161c78dd4b9a7b3ad78792a 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java diff --git a/patches/server/0096-Optimize-player-list-for-sending-player-info.patch b/patches/server/0096-Optimize-player-list-for-sending-player-info.patch index d777350..ac0bb68 100644 --- a/patches/server/0096-Optimize-player-list-for-sending-player-info.patch +++ b/patches/server/0096-Optimize-player-list-for-sending-player-info.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 23:30:38 +0100 Subject: [PATCH] Optimize player list for sending player info License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Spread out and optimise player list ticksSpread out and optimise player list ticks" @@ -11,6 +12,30 @@ By: James Lyne As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java index d2e6a92a44416bfaa161c78dd4b9a7b3ad78792a..a60db92d8c6afab40e12b3ac28241beac06bcf63 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java diff --git a/patches/server/0097-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch b/patches/server/0097-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch index 5129c24..7198060 100644 --- a/patches/server/0097-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch +++ b/patches/server/0097-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 00:07:46 +0100 Subject: [PATCH] Skip PlayerCommandSendEvent if there are no listeners License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Skip events if there's no listeners" @@ -11,6 +12,30 @@ By: William Blake Galbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java index 7c96f7fc5997761426a0c62cad0cab5cc668f282..89df1bb7a3f310e9fefef78e2645ddd50327c2ab 100644 --- a/src/main/java/net/minecraft/commands/Commands.java diff --git a/patches/server/0098-Send-multiple-keep-alive-packets.patch b/patches/server/0098-Send-multiple-keep-alive-packets.patch index 038cb83..8078a7b 100644 --- a/patches/server/0098-Send-multiple-keep-alive-packets.patch +++ b/patches/server/0098-Send-multiple-keep-alive-packets.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 00:43:42 +0100 Subject: [PATCH] Send multiple keep-alive packets License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Alternative Keepalive Handling" @@ -11,6 +12,30 @@ By: William Blake Galbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java index 9de597c11c3bd0f23e87c3a6187b2036987356e0..c171c272d5fcf0900514e18eafaa1b5ee019c74d 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java diff --git a/patches/server/0099-Prevent-entities-random-strolling-into-non-ticking-c.patch b/patches/server/0099-Prevent-entities-random-strolling-into-non-ticking-c.patch index 363b588..379ce40 100644 --- a/patches/server/0099-Prevent-entities-random-strolling-into-non-ticking-c.patch +++ b/patches/server/0099-Prevent-entities-random-strolling-into-non-ticking-c.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 20:38:15 +0100 Subject: [PATCH] Prevent entities random strolling into non-ticking chunks 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: "Don't wander into non-ticking chunks" diff --git a/patches/server/0100-Specific-interval-TPS-API.patch b/patches/server/0100-Specific-interval-TPS-API.patch index 1bac252..363bc2b 100644 --- a/patches/server/0100-Specific-interval-TPS-API.patch +++ b/patches/server/0100-Specific-interval-TPS-API.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 12:56:43 +0100 Subject: [PATCH] Specific interval TPS API 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 874f17fd7e3ff945eeccdf5e0b1991366240ea6b..1aa9a73aed2851cc640945101623f6f448079234 100644 diff --git a/patches/server/0101-5-second-TPS-average.patch b/patches/server/0101-5-second-TPS-average.patch index 105d4a2..4f7b296 100644 --- a/patches/server/0101-5-second-TPS-average.patch +++ b/patches/server/0101-5-second-TPS-average.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 12:42:32 +0100 Subject: [PATCH] 5 second TPS average License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Add 5 second tps average in /tps" @@ -11,6 +12,30 @@ By: William Blake Galbreath As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) +* Purpur copyright * + +MIT License + +Copyright (c) 2019-2022 PurpurMC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/com/destroystokyo/paper/gui/RAMDetails.java b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java index fa56cd09102a89692b42f1d14257990508c5c720..fac07dda413002c12276131efbe4ee3832c2265a 100644 --- a/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java diff --git a/patches/server/0102-Measure-last-tick-time.patch b/patches/server/0102-Measure-last-tick-time.patch index c916e61..8a0e977 100644 --- a/patches/server/0102-Measure-last-tick-time.patch +++ b/patches/server/0102-Measure-last-tick-time.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Sat, 26 Nov 2022 16:54:05 +0100 Subject: [PATCH] Measure last tick time License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Add getLastTickMs api" @@ -11,8 +12,18 @@ By: tr7zw As part of: YAPFA (https://github.com/tr7zw/YAPFA) Licensed under: MIT (https://opensource.org/licenses/MIT) +* YAPFA copyright * + +Copyright 2020 tr7zw + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +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/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index dccee7cb7075cba6877021ec0da1ba5603a00082..ec5e28a633abe6932fd8c1da47625309bee2ca03 100644 +index dccee7cb7075cba6877021ec0da1ba5603a00082..0b3b1400e9546060b4ee35236741670aaa226820 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -1095,6 +1095,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { return !this.canSleepForTickNoOversleep(); // Paper - move oversleep into full server tick }); -+ this.lastTickOversleepTime = (System.nanoTime() - tickOversleepStart) / 1000000L; // Gale - YAPFA - last tick time ++ lastTickOversleepTime = (System.nanoTime() - tickOversleepStart) / 1000000L; // Gale - YAPFA - last tick time } @Override diff --git a/patches/server/0103-Last-tick-time-API.patch b/patches/server/0103-Last-tick-time-API.patch index 17a224a..abf4782 100644 --- a/patches/server/0103-Last-tick-time-API.patch +++ b/patches/server/0103-Last-tick-time-API.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Sat, 26 Nov 2022 17:12:08 +0100 Subject: [PATCH] Last tick time API License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Add getLastTickMs api" @@ -11,6 +12,16 @@ By: tr7zw As part of: YAPFA (https://github.com/tr7zw/YAPFA) Licensed under: MIT (https://opensource.org/licenses/MIT) +* YAPFA copyright * + +Copyright 2020 tr7zw + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +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/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index cd685f4d0e5973b0c0a217c30235a4b88645694f..947ad1463a973546bdaf68654086291a3414aa9b 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java diff --git a/patches/server/0104-Show-last-tick-time-in-tps-command.patch b/patches/server/0104-Show-last-tick-time-in-tps-command.patch index 8a1add0..e98826c 100644 --- a/patches/server/0104-Show-last-tick-time-in-tps-command.patch +++ b/patches/server/0104-Show-last-tick-time-in-tps-command.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Sat, 26 Nov 2022 21:02:58 +0100 Subject: [PATCH] Show last tick time in /tps command License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Add getLastTickMs api" @@ -11,6 +12,16 @@ By: tr7zw As part of: YAPFA (https://github.com/tr7zw/YAPFA) Licensed under: MIT (https://opensource.org/licenses/MIT) +* YAPFA copyright * + +Copyright 2020 tr7zw + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +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/galemc/gale/configuration/GaleGlobalConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java index 024cf924592999726458976b4d73df4b71843a2e..eb1a5b20810cbad9f47505a8534a42b20b8653d5 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java diff --git a/patches/server/0105-Increase-time-statistics-in-intervals.patch b/patches/server/0105-Increase-time-statistics-in-intervals.patch index 6394a4d..d1d39d3 100644 --- a/patches/server/0105-Increase-time-statistics-in-intervals.patch +++ b/patches/server/0105-Increase-time-statistics-in-intervals.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 22:50:57 +0100 Subject: [PATCH] Increase time statistics in intervals License: MIT (https://opensource.org/licenses/MIT) +Gale - https://galemc.org This patch is based on the following patch: "Smarter statistics ticking" @@ -21,6 +22,28 @@ With an interval of 20, this patch saves roughly 3ms per tick on a server w/ 80 This patch was created for the Hydrinity project by Mykyta Komarnytskyy under the MIT license. +MIT License + +Copyright (c) 2020 Mykyta Komarnytskyy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java index 8818ca46af000926ed9eacb47953f440bdac1342..2bf1663dc1e969ea9e192c9941155eb99efff715 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java diff --git a/patches/server/0106-For-collision-check-has-physics-before-same-vehicle.patch b/patches/server/0106-For-collision-check-has-physics-before-same-vehicle.patch index d411dbd..5ae6e31 100644 --- a/patches/server/0106-For-collision-check-has-physics-before-same-vehicle.patch +++ b/patches/server/0106-For-collision-check-has-physics-before-same-vehicle.patch @@ -1,5 +1,5 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 00:30:17 +0100 Subject: [PATCH] For collision check has physics before same vehicle MIME-Version: 1.0 @@ -7,6 +7,7 @@ Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: "Swaps the predicate order of collision" diff --git a/patches/server/0107-Skip-negligible-planar-movement-multiplication.patch b/patches/server/0107-Skip-negligible-planar-movement-multiplication.patch index e4b9c5a..87b8377 100644 --- a/patches/server/0107-Skip-negligible-planar-movement-multiplication.patch +++ b/patches/server/0107-Skip-negligible-planar-movement-multiplication.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Wed, 30 Nov 2022 23:44:41 +0100 Subject: [PATCH] Skip negligible planar movement multiplication License: GPL-3.0 (https://www.gnu.org/licenses/gpl-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 f6b9c9c55effdc169d8226308b3163bc5c54b3ed..dbba8559117287932e7e400f03cd80ac7418c004 100644 diff --git a/patches/server/0109-Reduce-RandomSource-instances.patch b/patches/server/0108-Reduce-RandomSource-instances.patch similarity index 97% rename from patches/server/0109-Reduce-RandomSource-instances.patch rename to patches/server/0108-Reduce-RandomSource-instances.patch index b82cbff..f2a83de 100644 --- a/patches/server/0109-Reduce-RandomSource-instances.patch +++ b/patches/server/0108-Reduce-RandomSource-instances.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +From: Martijn Muijsers Date: Tue, 29 Nov 2022 00:45:45 +0100 Subject: [PATCH] Reduce RandomSource instances 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: "don't create new random instance" @@ -95,7 +96,7 @@ index d56e7e8cacdabb8f009eaba8164f65f4d824a282..c2dca83fb43244313c4ddaf93c5ded5f 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 31708d1e94b557896d42199aa0bc6ed26a4c6d8a..b47593d35ff55d40feee4e8b06deaa81da934f2b 100644 +index 31708d1e94b557896d42199aa0bc6ed26a4c6d8a..22ded3f273ea0c198a8caa33472fee11fc1a97cb 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java @@ -85,7 +85,7 @@ public class Explosion { @@ -103,7 +104,7 @@ index 31708d1e94b557896d42199aa0bc6ed26a4c6d8a..b47593d35ff55d40feee4e8b06deaa81 public Explosion(Level world, @Nullable Entity entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, double x, double y, double z, float power, boolean createFire, Explosion.BlockInteraction destructionType) { - this.random = RandomSource.create(); -+ this.random = world.random; // Gale - Patina - reduce RandomSource instances ++ this.random = world == null || world.random == null ? RandomSource.create() : world.random; // Gale - Patina - reduce RandomSource instances this.toBlow = new ObjectArrayList(); this.hitPlayers = Maps.newHashMap(); this.level = world; diff --git a/patches/server/0108-Variable-main-thread-task-delay.patch b/patches/server/0108-Variable-main-thread-task-delay.patch deleted file mode 100644 index e9c4c38..0000000 --- a/patches/server/0108-Variable-main-thread-task-delay.patch +++ /dev/null @@ -1,1108 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers -Date: Sun, 27 Nov 2022 16:32:36 +0100 -Subject: [PATCH] Variable main thread task delay - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) - -diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -index dabd93c35bdbac6a8b668a82d5f3d4173a1baa4a..da07ef850f54f1e0225a48ccf24e28e170935a7d 100644 ---- a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -21,6 +21,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.concurrent.MinecraftServerBlockableEventLoop; - - import java.util.*; - import java.util.concurrent.Executor; -@@ -180,7 +181,7 @@ public final class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockCo - - if (!Bukkit.isPrimaryThread()) { - // Plugins? -- MinecraftServer.getServer().scheduleOnMain(() -> modifyBlocks(chunkPacket, chunkPacketInfo)); -+ MinecraftServer.getServer().tell(() -> modifyBlocks(chunkPacket, chunkPacketInfo), MinecraftServerBlockableEventLoop.ANTI_XRAY_MODIFY_BLOCKS_TASK_MAX_DELAY); // Gale - main thread tasks with variable delay - return; - } - -diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java -index 8bc0cb9ad5bb4e76d962ff54305e2c08e279a17b..0ecccc1f6a17c9fb15c2ac1f33dcffc8d5a87cdd 100644 ---- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java -+++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java -@@ -2,6 +2,7 @@ package net.minecraft.network.protocol; - - import com.mojang.logging.LogUtils; - import net.minecraft.network.PacketListener; -+import org.galemc.gale.concurrent.AbstractBlockableEventLoop; - import org.slf4j.Logger; - - // CraftBukkit start -@@ -36,10 +37,10 @@ public class PacketUtils { - public PacketUtils() {} - - public static void ensureRunningOnSameThread(Packet packet, T listener, ServerLevel world) throws RunningOnDifferentThreadException { -- PacketUtils.ensureRunningOnSameThread(packet, listener, (BlockableEventLoop) world.getServer()); -+ PacketUtils.ensureRunningOnSameThread(packet, listener, world.getServer()); // Gale - main thread tasks with variable delay - } - -- 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 - main thread tasks with variable delay - if (!engine.isSameThread()) { - engine.executeIfPossible(() -> { - packetProcessing.push(listener); // Paper - detailed watchdog information -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index ec5e28a633abe6932fd8c1da47625309bee2ca03..9045ae692d77f986542940334dd15edef6b2a9f4 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -40,7 +40,6 @@ 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; -@@ -115,7 +114,6 @@ 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; -@@ -150,6 +148,7 @@ import net.minecraft.world.level.storage.loot.PredicateManager; - import net.minecraft.world.phys.Vec2; - import net.minecraft.world.phys.Vec3; - import org.apache.commons.lang3.Validate; -+import org.galemc.gale.concurrent.MinecraftServerBlockableEventLoop; - import org.galemc.gale.configuration.GaleConfigurations; - import org.galemc.gale.configuration.GaleGlobalConfiguration; - import org.slf4j.Logger; -@@ -174,9 +173,9 @@ import org.bukkit.event.server.ServerLoadEvent; - - 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 - main thread tasks with variable delay - -- private static MinecraftServer SERVER; // Paper -+ public static MinecraftServer SERVER; // Paper // Gale - main thread tasks with variable delay - private -> public - public static final Logger LOGGER = LogUtils.getLogger(); - public static final String VANILLA_BRAND = "vanilla"; - private static final float AVERAGE_TICK_TIME_SMOOTHING = 0.8F; -@@ -246,7 +245,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop public - private final PackRepository packRepository; - private final ServerScoreboard scoreboard; - @Nullable -@@ -314,7 +313,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop registryreadops, Thread thread, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PackRepository resourcepackrepository, WorldStem worldstem, Proxy proxy, DataFixer datafixer, Services services, ChunkProgressListenerFactory worldloadlistenerfactory) { -- super("Server"); -+ super(); // Gale - main thread tasks with variable delay - SERVER = this; // Paper - better singleton - this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; - this.profiler = this.metricsRecorder.getProfiler(); -@@ -893,7 +892,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(); -@@ -922,8 +921,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop public - // Paper start - if (this.forceTicks) { - return true; -@@ -1288,7 +1289,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop {}; -- } -- // 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 -- this.getProfiler().incrementCounter("runTask"); -- super.doRunTask(ticktask); -- } -- - private void updateStatusIcon(ServerStatus metadata) { - Optional optional = Optional.of(this.getFile("server-icon.png")).filter(File::isFile); - -@@ -1407,6 +1358,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= 5000000000L) { - this.lastServerStatus = i; -@@ -1455,7 +1407,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop CompletableFuture filterTextPacket(T text, BiFunction> filterer) { -@@ -895,13 +896,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 -+ server.tell(() -> this.disconnect(Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM), MinecraftServerBlockableEventLoop.KICK_FOR_COMMAND_PACKET_SPAM_TASK_MAX_DELAY); // Paper - kick event cause // Gale - JettPack - reduce array allocations // Gale - main thread tasks with variable delay - 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 -+ server.tell(() -> this.disconnect(Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM), MinecraftServerBlockableEventLoop.KICK_FOR_COMMAND_PACKET_SPAM_TASK_MAX_DELAY); // Paper - kick event cause // Gale - JettPack - reduce array allocations // Gale - main thread tasks with variable delay - return; - } - // Paper end -@@ -926,7 +927,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - if (!event.isHandled()) { - if (!event.isCancelled()) { - -- this.server.scheduleOnMain(() -> { // This needs to be on main -+ this.server.tell(() -> { // This needs to be on main // Gale - main thread tasks with variable delay - ParseResults parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack()); - - this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> { -@@ -937,7 +938,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestEvent.getSuggestions())); - // Paper end - Brigadier API - }); -- }); -+ }, MinecraftServerBlockableEventLoop.SEND_COMMAND_COMPLETION_SUGGESTIONS_TASK_MAX_DELAY); // Gale - main thread tasks with variable delay - } - } else if (!completions.isEmpty()) { - final com.mojang.brigadier.suggestion.SuggestionsBuilder builder0 = new com.mojang.brigadier.suggestion.SuggestionsBuilder(command, stringreader.getTotalLength()); -@@ -1246,7 +1247,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 -+ server.tell(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION), MinecraftServerBlockableEventLoop.KICK_FOR_BOOK_TOO_LARGE_PACKET_TASK_MAX_DELAY); // Paper - kick event cause // Gale - main thread tasks with variable delay - return; - } - byteTotal += byteLength; -@@ -1269,14 +1270,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 -+ server.tell(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION), MinecraftServerBlockableEventLoop.KICK_FOR_BOOK_TOO_LARGE_PACKET_TASK_MAX_DELAY); // Paper - kick event cause // Gale - main thread tasks with variable delay - 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 -+ server.tell(() -> this.disconnect("Book edited too quickly!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION), MinecraftServerBlockableEventLoop.KICK_FOR_EDITING_BOOK_TOO_QUICKLY_TASK_MAX_DELAY); // Paper - kick event cause // Paper - Also ensure this is called on main // Gale - main thread tasks with variable delay - return; - } - this.lastBookTick = MinecraftServer.currentTick; -@@ -2228,9 +2229,9 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - } - // CraftBukkit end - if (ServerGamePacketListenerImpl.isChatMessageIllegal(packet.message())) { -- this.server.scheduleOnMain(() -> { // Paper - push to main for event firing -+ this.server.tell(() -> { // Paper - push to main for event firing // Gale - main thread tasks with variable delay - 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 -+ }, MinecraftServerBlockableEventLoop.KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY); // Paper - push to main for event firing // Gale - main thread tasks with variable delay - } else { - if (this.tryHandleChat(packet.message(), packet.timeStamp(), packet.lastSeenMessages())) { - // this.server.submit(() -> { // CraftBukkit - async chat -@@ -2258,9 +2259,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 -+ this.server.tell(() -> { // Paper - push to main for event firing // Gale - main thread tasks with variable delay - this.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper -- }); // Paper - push to main for event firing -+ }, MinecraftServerBlockableEventLoop.KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY); // Paper - push to main for event firing // Gale - main thread tasks with variable delay - } else { - if (this.tryHandleChat(packet.command(), packet.timeStamp(), packet.lastSeenMessages())) { - this.server.submit(() -> { -@@ -2357,9 +2358,9 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - private boolean tryHandleChat(String message, Instant timestamp, LastSeenMessages.Update acknowledgment) { - if (!this.updateChatOrder(timestamp)) { - ServerGamePacketListenerImpl.LOGGER.warn("{} sent out-of-order chat: '{}': {} > {}", this.player.getName().getString(), message, this.lastChatTimeStamp.get().getEpochSecond(), timestamp.getEpochSecond()); // Paper -- this.server.scheduleOnMain(() -> { // Paper - push to main -+ this.server.tell(() -> { // Paper - push to main // Gale - main thread tasks with variable delay - this.disconnect(Component.translatable("multiplayer.disconnect.out_of_order_chat"), org.bukkit.event.player.PlayerKickEvent.Cause.OUT_OF_ORDER_CHAT); // Paper - kick event cause -- }); // Paper - push to main -+ }, MinecraftServerBlockableEventLoop.KICK_FOR_OUT_OF_ORDER_CHAT_PACKET_TASK_MAX_DELAY); // Paper - push to main // Gale - main thread tasks with variable delay - return false; - } else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales - this.send(new ClientboundSystemChatPacket(Component.translatable("chat.disabled.options").withStyle(ChatFormatting.RED), false)); -@@ -3420,7 +3421,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 -+ server.tell(() -> this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM), MinecraftServerBlockableEventLoop.KICK_FOR_RECIPE_PACKET_SPAM_TASK_MAX_DELAY); // Paper - kick event cause // Gale - JettPack - reduce array allocations // Gale - main thread tasks with variable delay - return; - } - } -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index a60db92d8c6afab40e12b3ac28241beac06bcf63..a9f5ce015e1f99391c8ba91cbe0c91de327c693f 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -100,6 +100,7 @@ import net.minecraft.world.scores.Objective; - import net.minecraft.world.scores.PlayerTeam; - import net.minecraft.world.scores.Scoreboard; // Paper - import net.minecraft.world.scores.Team; -+import org.galemc.gale.concurrent.MinecraftServerBlockableEventLoop; - import org.galemc.gale.configuration.GaleGlobalConfiguration; - import org.slf4j.Logger; - -@@ -306,7 +307,7 @@ public abstract class PlayerList { - worldserver1, chunkX, chunkZ, net.minecraft.server.level.ChunkHolder.FullChunkStatus.ENTITY_TICKING, true, - ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHEST, - (chunk) -> { -- MinecraftServer.getServer().scheduleOnMain(() -> { -+ MinecraftServer.getServer().tell(() -> { // Gale - main thread tasks with variable delay - try { - if (!playerconnection.connection.isConnected()) { - return; -@@ -319,7 +320,7 @@ public abstract class PlayerList { - } finally { - finalWorldserver.pendingLogin.remove(player); - } -- }); -+ }, MinecraftServerBlockableEventLoop.POST_CHUNK_LOAD_JOIN_TASK_MAX_DELAY); // Gale - main thread tasks with variable delay - } - ); - } -diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -index 83701fbfaa56a232593ee8f11a3afb8941238bfa..30e6da28c30b169763417b271dbe72cc6bdba1ea 100644 ---- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -+++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -@@ -10,13 +10,16 @@ import java.util.concurrent.Executor; - import java.util.concurrent.locks.LockSupport; - import java.util.function.BooleanSupplier; - import java.util.function.Supplier; -+ -+import net.minecraft.server.MinecraftServer; - 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.concurrent.AbstractBlockableEventLoop; - import org.slf4j.Logger; - --public abstract class BlockableEventLoop implements ProfilerMeasured, ProcessorHandle, Executor { -+public abstract class BlockableEventLoop implements ProfilerMeasured, ProcessorHandle, AbstractBlockableEventLoop { // Gale - main thread tasks with variable delay - private final String name; - private static final Logger LOGGER = LogUtils.getLogger(); - private final Queue pendingRunnables = Queues.newConcurrentLinkedQueue(); -@@ -31,6 +34,7 @@ public abstract class BlockableEventLoop implements Profiler - - protected abstract boolean shouldRun(R task); - -+ @Override // Gale - main thread tasks with variable delay - public boolean isSameThread() { - return Thread.currentThread() == this.getRunningThread(); - } -@@ -102,6 +106,7 @@ public abstract class BlockableEventLoop implements Profiler - - } - -+ @Override // Gale - main thread tasks with variable delay - public void executeIfPossible(Runnable runnable) { - this.execute(runnable); - } -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 55d83a9a691d11c9408d2c3260c3e77dfb51b97e..f8a5bac64d9877ba18d18cc341fd7a3f4a8a6a0d 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -135,6 +135,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.concurrent.MinecraftServerBlockableEventLoop; - - public class CraftWorld extends CraftRegionAccessor implements World { - public static final int CUSTOM_DIMENSION_OFFSET = 10; -@@ -2353,11 +2354,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(() -> { -+ net.minecraft.server.MinecraftServer.getServer().tell(() -> { // Gale - main thread tasks with variable delay - net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)c; - if (chunk != null) addTicket(x, z); // Paper - ret.complete(chunk == null ? null : chunk.getBukkitChunk()); -- }); -+ }, MinecraftServerBlockableEventLoop.COMPLETE_CHUNK_FUTURE_TASK_MAX_DELAY); // Gale - main thread tasks with variable delay - }); - - 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 9368ec01e498f913bc5b7b3e77fe87659090d9b5..ee20bf57734bcd24f7c898cd52efea70b9a7d45f 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -189,6 +189,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.concurrent.MinecraftServerBlockableEventLoop; - - public abstract class CraftEntity implements org.bukkit.entity.Entity { - private static PermissibleBase perm; -@@ -1260,7 +1261,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(() -> { -+ net.minecraft.server.MinecraftServer.getServer().tell(() -> { // Gale - main thread tasks with variable delay - try { - ret.complete(CraftEntity.this.teleport(locationClone, cause) ? Boolean.TRUE : Boolean.FALSE); - } catch (Throwable throwable) { -@@ -1270,7 +1271,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); - } -- }); -+ }, MinecraftServerBlockableEventLoop.TELEPORT_ASYNC_TASK_MAX_DELAY); // Gale - main thread tasks with variable delay - }); - - return ret; -diff --git a/src/main/java/org/galemc/gale/concurrent/AbstractBlockableEventLoop.java b/src/main/java/org/galemc/gale/concurrent/AbstractBlockableEventLoop.java -new file mode 100644 -index 0000000000000000000000000000000000000000..5c7f2159756a3de250b10b82922d69c6fab882ec ---- /dev/null -+++ b/src/main/java/org/galemc/gale/concurrent/AbstractBlockableEventLoop.java -@@ -0,0 +1,20 @@ -+// Gale - main thread tasks with variable delay -+ -+package org.galemc.gale.concurrent; -+ -+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 -+ */ -+public interface AbstractBlockableEventLoop extends Executor { -+ -+ boolean isSameThread(); -+ -+ void executeIfPossible(Runnable runnable); -+ -+} -diff --git a/src/main/java/org/galemc/gale/concurrent/MinecraftServerBlockableEventLoop.java b/src/main/java/org/galemc/gale/concurrent/MinecraftServerBlockableEventLoop.java -new file mode 100644 -index 0000000000000000000000000000000000000000..4b82aea23b99180f13c71a1797c4d829045872f5 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/concurrent/MinecraftServerBlockableEventLoop.java -@@ -0,0 +1,324 @@ -+// Gale - main thread tasks with variable delay -+ -+package org.galemc.gale.concurrent; -+ -+import com.mojang.logging.LogUtils; -+import io.papermc.paper.util.TickThread; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.util.thread.BlockableEventLoop; -+import net.minecraft.util.thread.ProcessorHandle; -+import net.minecraft.util.thread.ReentrantBlockableEventLoop; -+import org.slf4j.Logger; -+ -+import java.util.Iterator; -+import java.util.Queue; -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.Executor; -+import java.util.concurrent.RejectedExecutionException; -+import java.util.concurrent.locks.Lock; -+import java.util.concurrent.locks.LockSupport; -+import java.util.concurrent.locks.ReadWriteLock; -+import java.util.concurrent.locks.ReentrantReadWriteLock; -+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 -+ */ -+public class MinecraftServerBlockableEventLoop implements ProcessorHandle, AbstractBlockableEventLoop { -+ -+ 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; -+ -+ private static final String NAME = "Server"; -+ private static final Logger LOGGER = LogUtils.getLogger(); -+ -+ /** -+ * A number of queues, which the queue at index i being the queue to be used after another i ticks pass -+ * even when {@link MinecraftServer#haveTime()} is false. -+ */ -+ public static Queue[] pendingRunnables = new Queue[0]; -+ -+ public static volatile int firstQueueWithElementsIndex = 0; -+ public static volatile long stamp = 0; -+ -+ private static final ReadWriteLock pendingRunnablesLock = new ReentrantReadWriteLock(); -+ private static final Lock pendingRunnablesReadLock = pendingRunnablesLock.readLock(); -+ public static final Lock pendingRunnablesWriteLock = pendingRunnablesLock.writeLock(); -+ -+ private static int blockingCount; -+ private static int reentrantCount; -+ -+ public static boolean scheduleExecutables() { -+ return (reentrantCount != 0 || !(Thread.currentThread() instanceof TickThread)) && !MinecraftServer.SERVER.isStopped(); -+ } -+ -+ protected boolean runningTask() { -+ return reentrantCount != 0; -+ } -+ -+ /** -+ * This moves the queues in {@link #pendingRunnables}. -+ */ -+ public static void tickPendingRunnables() { -+ while (!pendingRunnablesWriteLock.tryLock()) {} -+ try { -+ stamp++; -+ // Move the queues to the preceding position -+ Queue firstQueue = pendingRunnables[0]; -+ for (int i = 1; i < pendingRunnables.length; i++) { -+ pendingRunnables[i - 1] = pendingRunnables[i]; -+ } -+ // Re-use the same instance that was the old first queue as the new last queue -+ pendingRunnables[pendingRunnables.length - 1] = firstQueue; -+ // Move any elements that were still present in the previous first queue to the new first queue -+ pendingRunnables[0].addAll(firstQueue); -+ firstQueue.clear(); -+ stamp++; -+ } finally { -+ pendingRunnablesWriteLock.unlock(); -+ } -+ } -+ -+ private static boolean hasPendingTasks() { -+ while (!pendingRunnablesReadLock.tryLock()); -+ try { -+ for (int i = 0; i < pendingRunnables.length; i++) { -+ if (!pendingRunnables[i].isEmpty()) { -+ return true; -+ } -+ } -+ return false; -+ } finally { -+ pendingRunnablesReadLock.unlock(); -+ } -+ } -+ -+ public int getPendingTasksCount() { -+ while (!pendingRunnablesReadLock.tryLock()); -+ try { -+ int count = 0; -+ for (int i = 0; i < pendingRunnables.length; i++) { -+ count += pendingRunnables[i].size(); -+ } -+ return count; -+ } finally { -+ pendingRunnablesReadLock.unlock(); -+ } -+ } -+ -+ 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((Void)null); -+ } -+ } -+ -+ public void executeBlocking(Runnable runnable) { -+ if (Thread.currentThread() != MinecraftServer.SERVER.serverThread) { -+ this.submitAsync(runnable).join(); -+ } else { -+ runnable.run(); -+ } -+ -+ } -+ -+ @Override -+ public void tell(Runnable runnable) { -+ this.tell(runnable, DEFAULT_TASK_MAX_DELAY); -+ } -+ -+ public void tell(Runnable runnable, 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) { -+ runnable.run(); -+ return; -+ } -+ // Paper end -+ while (!pendingRunnablesReadLock.tryLock()) {} -+ try { -+ stamp++; -+ this.pendingRunnables[maxDelay].add(runnable); -+ if (maxDelay < firstQueueWithElementsIndex) { -+ firstQueueWithElementsIndex = maxDelay; -+ } -+ LockSupport.unpark(MinecraftServer.SERVER.serverThread); -+ stamp++; -+ } finally { -+ pendingRunnablesReadLock.unlock(); -+ } -+ } -+ -+ @Override -+ public void execute(Runnable runnable) { -+ if (scheduleExecutables()) { -+ this.tell(runnable); -+ } else { -+ runnable.run(); -+ } -+ -+ } -+ -+ @Override -+ public boolean isSameThread() { -+ return Thread.currentThread() instanceof TickThread; -+ } -+ -+ @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. -+ */ -+ protected void runAllTasksForAllTicks() { -+ while (this.pollTask(false) || hasPendingTasks()) {} -+ } -+ -+ /** -+ * 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. -+ */ -+ protected void runAllTasksWithinTimeOrForCurrentTick() { -+ while (this.pollTask(true) || !pendingRunnables[0].isEmpty()) {} -+ } -+ -+ public boolean pollTask(boolean mustBeWithinTimeOrForCurrentTick) { -+ if (this.pollTaskInternal(mustBeWithinTimeOrForCurrentTick)) { -+ MinecraftServer.SERVER.executeMidTickTasks(); // Paper - execute chunk tasks mid tick -+ MinecraftServer.SERVER.mayHaveDelayedTasks = true; -+ return true; -+ } else { -+ boolean ret = false; // Paper - force execution of all worlds, do not just bias the first -+ if (MinecraftServer.SERVER.haveTime()) { -+ Iterator iterator = MinecraftServer.SERVER.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 -+ } -+ } -+ } -+ -+ MinecraftServer.SERVER.mayHaveDelayedTasks = ret; -+ return ret; // Paper - force execution of all worlds, do not just bias the first -+ } -+ } -+ -+ public boolean pollTaskInternal(boolean mustBeWithinTimeOrForCurrentTick) { -+ mustBeWithinTimeOrForCurrentTick &= blockingCount == 0; // If we are in some way within a managedBlock call, we just want to run some tasks to fill up the time, regardless of whether there is tick time left or what moment a task is supposed to be finished by -+ while (true) { -+ // Make sure we use the first queue if it is non-empty -+ Runnable runnable = (Runnable) pendingRunnables[0].peek(); -+ int indexThatRunnableIsFrom = 0; -+ // Keep trying to get a runnable -+ if (runnable == null) { -+ tryGetRunnableUntilSuccessOrNothingChanged: while (true) { -+ int oldFirstQueueWithElementsIndex = firstQueueWithElementsIndex; -+ long oldStamp = stamp; -+ // First try from firstQueueWithElementsIndex until the end of pendingRunnables, then try all pendingRunnables again just in case -+ for (int attempt = 0; attempt < 2; attempt++) { -+ for (int i = attempt == 0 ? oldFirstQueueWithElementsIndex : 0; i < pendingRunnables.length; i++) { -+ runnable = (Runnable) pendingRunnables[i].peek(); -+ if (runnable != null) { -+ if (attempt == 0 ? i > firstQueueWithElementsIndex : i < firstQueueWithElementsIndex) { -+ firstQueueWithElementsIndex = i; -+ } -+ indexThatRunnableIsFrom = i; -+ break tryGetRunnableUntilSuccessOrNothingChanged; -+ } -+ } -+ } -+ if (oldFirstQueueWithElementsIndex == firstQueueWithElementsIndex && oldStamp == stamp) { // If we did not find an element and nothing has changed, we give up -+ return false; -+ } -+ } -+ } -+ if (mustBeWithinTimeOrForCurrentTick && !(indexThatRunnableIsFrom == 0 || MinecraftServer.SERVER.haveTime() || !pendingRunnables[0].isEmpty())) { -+ return false; -+ } else { -+ runnable = (Runnable) pendingRunnables[indexThatRunnableIsFrom].poll(); -+ if (runnable == null) { -+ // We lost a race condition between the peek() and poll() calls: just try again -+ continue; -+ } -+ doRunTask(runnable); -+ return true; -+ } -+ } -+ } -+ -+ public void managedBlock(BooleanSupplier stopCondition) { -+ ++blockingCount; -+ -+ try { -+ while(!stopCondition.getAsBoolean()) { -+ if (!this.pollTask(false)) { -+ this.waitForTasks(); -+ } -+ } -+ } finally { -+ --blockingCount; -+ } -+ -+ } -+ -+ private static void waitForTasks() { -+ Thread.yield(); -+ LockSupport.parkNanos("waiting for tasks", 100000L); -+ } -+ -+ private static void doRunTask(Runnable task) { -+ MinecraftServer.SERVER.getProfiler().incrementCounter("runTask"); -+ ++reentrantCount; -+ try { -+ task.run(); -+ } catch (Exception var3) { -+ if (var3.getCause() instanceof ThreadDeath) throw var3; // Paper -+ LOGGER.error(LogUtils.FATAL_MARKER, "Error executing task on {}", NAME, var3); -+ } finally { -+ --reentrantCount; -+ } -+ } -+ -+ @Override -+ public String name() { -+ return NAME; -+ } -+ -+} -diff --git a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java -index ef430bc8b0527b919c91361aa9ddefc8eb2386d8..6717384db82ac8615b8e9c8490fd6f3f3eae1ec9 100644 ---- a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java -+++ b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java -@@ -2,11 +2,14 @@ - - package org.galemc.gale.configuration; - -+import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue; - import io.papermc.paper.configuration.Configuration; - import io.papermc.paper.configuration.ConfigurationPart; - import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -+import org.galemc.gale.concurrent.MinecraftServerBlockableEventLoop; - import org.spongepowered.configurate.objectmapping.meta.Setting; - -+import java.util.Arrays; - import java.util.Locale; - import java.util.function.Consumer; - -@@ -74,6 +77,223 @@ public class GaleGlobalConfiguration extends ConfigurationPart { - } - // Gale end - Pufferfish - SIMD support - -+ // Gale start - main thread tasks with variable delay -+ public MainThreadTaskMaxDelay mainThreadTaskMaxDelay; -+ public class MainThreadTaskMaxDelay extends ConfigurationPart.Post { -+ -+ /** -+ * The default maximum delay for tasks. -+ * Given in ticks. -+ * Any value < 0 uses the vanilla maximum delay for tasks, which is currently 2. -+ *
    -+ *
  • 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 (!MinecraftServerBlockableEventLoop.pendingRunnablesWriteLock.tryLock()) {} -+ try { -+ // Update the values in MinecraftServerBlockableEventLoop for quick access -+ MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY = this.defaultValue >= 0 ? this.defaultValue : 2; -+ MinecraftServerBlockableEventLoop.COMPLETE_CHUNK_FUTURE_TASK_MAX_DELAY = this.completeChunkFuture >= 0 ? this.completeChunkFuture : MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY; -+ MinecraftServerBlockableEventLoop.POST_CHUNK_LOAD_JOIN_TASK_MAX_DELAY = this.postChunkLoadJoin >= 0 ? this.postChunkLoadJoin : MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY; -+ MinecraftServerBlockableEventLoop.ANTI_XRAY_MODIFY_BLOCKS_TASK_MAX_DELAY = this.antiXrayModifyBlocks >= 0 ? this.antiXrayModifyBlocks : MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY; -+ MinecraftServerBlockableEventLoop.TELEPORT_ASYNC_TASK_MAX_DELAY = this.teleportAsync >= 0 ? this.teleportAsync : MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY; -+ MinecraftServerBlockableEventLoop.SEND_COMMAND_COMPLETION_SUGGESTIONS_TASK_MAX_DELAY = this.sendCommandCompletionSuggestions >= 0 ? this.sendCommandCompletionSuggestions : MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY; -+ MinecraftServerBlockableEventLoop.KICK_FOR_COMMAND_PACKET_SPAM_TASK_MAX_DELAY = this.kickForCommandPacketSpam >= 0 ? this.kickForCommandPacketSpam : MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY; -+ MinecraftServerBlockableEventLoop.KICK_FOR_RECIPE_PACKET_SPAM_TASK_MAX_DELAY = this.kickForRecipePacketSpam >= 0 ? this.kickForRecipePacketSpam : MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY; -+ MinecraftServerBlockableEventLoop.KICK_FOR_BOOK_TOO_LARGE_PACKET_TASK_MAX_DELAY = this.kickForBookTooLargePacket >= 0 ? this.kickForBookTooLargePacket : MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY; -+ MinecraftServerBlockableEventLoop.KICK_FOR_EDITING_BOOK_TOO_QUICKLY_TASK_MAX_DELAY = this.kickForEditingBookTooQuickly >= 0 ? this.kickForEditingBookTooQuickly : MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY; -+ MinecraftServerBlockableEventLoop.KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY = this.kickForIllegalCharactersInChatPacket >= 0 ? this.kickForIllegalCharactersInChatPacket : MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY; -+ MinecraftServerBlockableEventLoop.KICK_FOR_OUT_OF_ORDER_CHAT_PACKET_TASK_MAX_DELAY = this.kickForOutOfOrderChatPacket >= 0 ? this.kickForOutOfOrderChatPacket : MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY; -+ MinecraftServerBlockableEventLoop.HANDLE_DISCONNECT_TASK_MAX_DELAY = this.handleDisconnect >= 0 ? this.handleDisconnect : MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY; -+ // Change the length of the pendingRunnables array of queues -+ int maxDelay = 0; -+ for (int delay : new int[]{ -+ MinecraftServerBlockableEventLoop.DEFAULT_TASK_MAX_DELAY, -+ MinecraftServerBlockableEventLoop.COMPLETE_CHUNK_FUTURE_TASK_MAX_DELAY, -+ MinecraftServerBlockableEventLoop.POST_CHUNK_LOAD_JOIN_TASK_MAX_DELAY, -+ MinecraftServerBlockableEventLoop.ANTI_XRAY_MODIFY_BLOCKS_TASK_MAX_DELAY, -+ MinecraftServerBlockableEventLoop.TELEPORT_ASYNC_TASK_MAX_DELAY, -+ MinecraftServerBlockableEventLoop.SEND_COMMAND_COMPLETION_SUGGESTIONS_TASK_MAX_DELAY, -+ MinecraftServerBlockableEventLoop.KICK_FOR_COMMAND_PACKET_SPAM_TASK_MAX_DELAY, -+ MinecraftServerBlockableEventLoop.KICK_FOR_RECIPE_PACKET_SPAM_TASK_MAX_DELAY, -+ MinecraftServerBlockableEventLoop.KICK_FOR_BOOK_TOO_LARGE_PACKET_TASK_MAX_DELAY, -+ MinecraftServerBlockableEventLoop.KICK_FOR_EDITING_BOOK_TOO_QUICKLY_TASK_MAX_DELAY, -+ MinecraftServerBlockableEventLoop.KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY, -+ MinecraftServerBlockableEventLoop.KICK_FOR_OUT_OF_ORDER_CHAT_PACKET_TASK_MAX_DELAY, -+ MinecraftServerBlockableEventLoop.HANDLE_DISCONNECT_TASK_MAX_DELAY -+ }) { -+ if (delay > maxDelay) { -+ maxDelay = delay; -+ } -+ } -+ int newPendingRunnablesLength = maxDelay + 1; -+ int oldPendingRunnablesLength = MinecraftServerBlockableEventLoop.pendingRunnables.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 < MinecraftServerBlockableEventLoop.pendingRunnables.length; i++) { -+ MinecraftServerBlockableEventLoop.pendingRunnables[maxDelay].addAll(MinecraftServerBlockableEventLoop.pendingRunnables[i]); -+ } -+ // Update the first queue with elements index -+ if (MinecraftServerBlockableEventLoop.firstQueueWithElementsIndex >= newPendingRunnablesLength) { -+ MinecraftServerBlockableEventLoop.firstQueueWithElementsIndex = maxDelay; -+ } -+ } -+ MinecraftServerBlockableEventLoop.pendingRunnables = Arrays.copyOf(MinecraftServerBlockableEventLoop.pendingRunnables, newPendingRunnablesLength); -+ if (newPendingRunnablesLength > oldPendingRunnablesLength) { -+ // Create new queues -+ for (int i = oldPendingRunnablesLength; i < newPendingRunnablesLength; i++) { -+ MinecraftServerBlockableEventLoop.pendingRunnables[i] = new MultiThreadedQueue<>(); -+ } -+ } -+ } -+ } finally { -+ MinecraftServerBlockableEventLoop.pendingRunnablesWriteLock.unlock(); -+ } -+ } -+ -+ } -+ // Gale end - main thread tasks with variable delay -+ - } - - public LogToConsole logToConsole; diff --git a/patches/server/0109-Thread-safety-annotations.patch b/patches/server/0109-Thread-safety-annotations.patch new file mode 100644 index 0000000..5457736 --- /dev/null +++ b/patches/server/0109-Thread-safety-annotations.patch @@ -0,0 +1,535 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martijn Muijsers +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..d07f68ff73a368c8f0da56152021a95474a601ca +--- /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, as well as 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/AnyThreadSafe.java b/src/main/java/org/galemc/gale/executor/annotation/AnyThreadSafe.java +new file mode 100644 +index 0000000000000000000000000000000000000000..01d9d667b86c8e42444ca73d6ee089f9570b5651 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/annotation/AnyThreadSafe.java +@@ -0,0 +1,40 @@ ++// Gale - thread-safety annotations ++ ++package org.galemc.gale.executor.annotation; ++ ++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 safely called from any thread, ++ * including but not limited to threads that are an instance of {@link BaseThread}. ++ *
++ * 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 threads that are not ++ * instances of {@link BaseThread} 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/BaseThreadOnly.java b/src/main/java/org/galemc/gale/executor/annotation/BaseThreadOnly.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ebca19c8934722f9a60a4e26f893c630a3b088f9 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/annotation/BaseThreadOnly.java +@@ -0,0 +1,39 @@ ++// Gale - thread-safety annotations ++ ++package org.galemc.gale.executor.annotation; ++ ++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}. ++ *
++ * When applied to a field, this annotation indicates that the field can only be accessed and/or modified ++ * on a thread that is an instance of {@link BaseThread}. ++ *
++ * 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 BaseThreadOnly}, fields and methods annotated with {@link BaseThreadOnly}, ++ * {@link BaseThreadSafe} or {@link AnyThreadSafe} may all be used. ++ *
++ * Methods that are potentially blocking, e.g. those annotated with {@link PotentiallyBlocking}, must never ++ * be called from a method annotated with {@link BaseThreadOnly}. ++ * ++ * @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/BaseThreadSafe.java b/src/main/java/org/galemc/gale/executor/annotation/BaseThreadSafe.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a3b0469cbfc8c7d369595359704b86cba222a451 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/annotation/BaseThreadSafe.java +@@ -0,0 +1,40 @@ ++// Gale - thread-safety annotations ++ ++package org.galemc.gale.executor.annotation; ++ ++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 safely called from any thread ++ * that is an instance of {@link BaseThread}. ++ *
++ * When applied to a field, this annotation indicates that the field can be safely arbitrarily accessed and/or modified ++ * from any thread that is an instance of {@link BaseThread}. ++ *
++ * 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 BaseThreadSafe}, the only fields and methods that can be used are those ++ * annotated with {@link BaseThreadSafe} or {@link AnyThreadSafe}, ++ * unless extra safety guarantees are checked or explicitly provided. ++ *
++ * Methods that are potentially blocking, e.g. those annotated with {@link PotentiallyBlocking}, must never ++ * be called from a method annotated with {@link BaseThreadSafe}. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@Documented ++@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD}) ++public @interface BaseThreadSafe { ++ ++ /** ++ * @see ThreadRestricted#fieldAccess() ++ */ ++ Access value() default Access.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..059eef051c2033ef70fb0e651ecb16e46961ba80 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/annotation/Guarded.java +@@ -0,0 +1,43 @@ ++// 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 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 ++@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 ""; ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/annotation/MainThreadOnly.java b/src/main/java/org/galemc/gale/executor/annotation/MainThreadOnly.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c22c3bd52caa8a50bce27c6fd90c26153965424d +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/annotation/MainThreadOnly.java +@@ -0,0 +1,42 @@ ++// Gale - thread-safety annotations ++ ++package org.galemc.gale.executor.annotation; ++ ++import org.galemc.gale.executor.thread.MainThreadClaim; ++ ++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 the main thread, ++ * as defined by {@link MainThreadClaim}. ++ *
++ * When applied to a field, this annotation indicates that the field can only be accessed and/or modified ++ * on the main 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 MainThreadOnly}, fields and methods annotated with {@link MainThreadOnly}, ++ * {@link BaseThreadOnly}, {@link BaseThreadSafe} or {@link AnyThreadSafe} may all be used. Use of methods annotated ++ * with {@link PotentiallyYielding} is inevitable, but discouraged, because it may lower the number of main-thread-only ++ * tasks that can be processed in the same amount of time (if some of that time is not spent on the task itself ++ * but on waiting for something - whether by blocking or yielding). ++ *
++ * Methods that are potentially blocking, e.g. those annotated with {@link PotentiallyBlocking}, must never ++ * be called from a method annotated with {@link BaseThreadSafe}. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@Documented ++@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD}) ++public @interface MainThreadOnly { ++ ++ /** ++ * @see ThreadRestricted#fieldAccess() ++ */ ++ Access value() default Access.READ_WRITE; ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/annotation/OriginalServerThreadOnly.java b/src/main/java/org/galemc/gale/executor/annotation/OriginalServerThreadOnly.java +new file mode 100644 +index 0000000000000000000000000000000000000000..67f62207febb7348dcbf121f1d0154dfb3472945 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/annotation/OriginalServerThreadOnly.java +@@ -0,0 +1,30 @@ ++// Gale - thread-safety annotations ++ ++package org.galemc.gale.executor.annotation; ++ ++import net.minecraft.server.MinecraftServer; ++ ++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 MinecraftServer#originalServerThread}. ++ *
++ * This annotation can also be used on fields, similar to {@link ThreadRestricted}. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ * ++ * @see ThreadRestricted ++ */ ++@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/PotentiallyBlocking.java b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java +new file mode 100644 +index 0000000000000000000000000000000000000000..71f26852c96dea34ea07efe07f834f8262509957 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java +@@ -0,0 +1,39 @@ ++// Gale - thread-safety annotations ++ ++package org.galemc.gale.executor.annotation; ++ ++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 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. ++ *
++ * Methods that are potentially blocking, including those annotated with {@link PotentiallyBlocking}, must never ++ * be called on a {@link BaseThread}. ++ * ++ * @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..e87ee2612348fc559b21256cc7cadfc684f01f8e +--- /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, 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 ++ * 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/ThisBaseThreadOnly.java b/src/main/java/org/galemc/gale/executor/annotation/ThisBaseThreadOnly.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a88eff774d0e289b210ef5b9a5ffd16c913dac33 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/annotation/ThisBaseThreadOnly.java +@@ -0,0 +1,32 @@ ++// Gale - thread-safety annotations ++ ++package org.galemc.gale.executor.annotation; ++ ++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 from the ++ * enclosing {@link BaseThread} instance. ++ *
++ * This annotation can also be used on fields, similar to {@link ThreadRestricted}. ++ *
++ * This annotation can only be used within {@link BaseThread} or subclasses thereof. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ * ++ * @see ThreadRestricted ++ */ ++@Documented ++@Target({ElementType.METHOD, ElementType.FIELD}) ++public @interface ThisBaseThreadOnly { ++ ++ /** ++ * @see ThreadRestricted#fieldAccess() ++ */ ++ Access value() default Access.READ_WRITE; ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/annotation/ThreadRestricted.java b/src/main/java/org/galemc/gale/executor/annotation/ThreadRestricted.java +new file mode 100644 +index 0000000000000000000000000000000000000000..773dcc2dc2a72b99d415c2e52b363ae24e9bb018 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/annotation/ThreadRestricted.java +@@ -0,0 +1,49 @@ ++// Gale - thread-safety annotations ++ ++package org.galemc.gale.executor.annotation; ++ ++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 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 instances of {@link BaseThread}, is discouraged, because threads that are not instances of ++ * {@link BaseThread} 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/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/patches/server/0110-CPU-cores-estimation.patch b/patches/server/0110-CPU-cores-estimation.patch index 41525a6..e7a77cd 100644 --- a/patches/server/0110-CPU-cores-estimation.patch +++ b/patches/server/0110-CPU-cores-estimation.patch @@ -1,9 +1,10 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers +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/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java index 8e45f712968303b7864d61adbf7325142c83f582..b374f41b20b3ffa2ec2874a06715661f4fec83db 100644 @@ -44,28 +45,29 @@ index 947ad1463a973546bdaf68654086291a3414aa9b..69bde99acff7bdae9af7cfe60e222167 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 a6de617fa261aa1916afc412ac85edb0c2630cab..9ad030004a3ca79be3f8ffdaa5aa4d95ffe4cab7 100644 +index 8aaa6e3a6c475258bea5f026a7e07efde825f112..6f6b96fe499f1b1888a141b60bd3773ba45e75f6 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java +++ b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java -@@ -305,6 +305,7 @@ public class GaleGlobalConfiguration extends ConfigurationPart { +@@ -85,6 +85,7 @@ public class GaleGlobalConfiguration extends ConfigurationPart { public boolean setBlockInFarChunk = true; // Gale - Purpur - do not log setBlock in far chunks 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 cpuCoresEstimation = true; // Gale - CPU core estimation ++ public boolean cpuCoresEstimation = true; // 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..2095cef565af83869f30504a3609601365149726 +index 0000000000000000000000000000000000000000..aa93b9f8015b98123411c56c148e5b61e34a912d --- /dev/null +++ b/src/main/java/org/galemc/gale/util/CPUCoresEstimation.java -@@ -0,0 +1,99 @@ -+// Gale - CPU core estimation +@@ -0,0 +1,102 @@ ++// Gale - CPU cores estimation + +package org.galemc.gale.util; + -+import com.mojang.logging.LogUtils; ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.YieldFree; +import org.slf4j.Logger; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; @@ -74,8 +76,10 @@ index 0000000000000000000000000000000000000000..2095cef565af83869f30504a36096013 +/** + * A utility class to estimate the number of physical CPU cores. + * -+ * @author Martijn Muijsers ++ * @author Martijn Muijsers under AGPL-3.0 + */ ++@AnyThreadSafe ++@YieldFree +public final class CPUCoresEstimation { + + private CPUCoresEstimation() {} diff --git a/patches/server/0111-Add-centralized-AsyncExecutor.patch b/patches/server/0111-Add-centralized-AsyncExecutor.patch deleted file mode 100644 index c5f5634..0000000 --- a/patches/server/0111-Add-centralized-AsyncExecutor.patch +++ /dev/null @@ -1,116 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers -Date: Tue, 29 Nov 2022 12:35:35 +0100 -Subject: [PATCH] Add centralized AsyncExecutor - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) - -diff --git a/src/main/java/org/galemc/gale/concurrent/AsyncExecutor.java b/src/main/java/org/galemc/gale/concurrent/AsyncExecutor.java -new file mode 100644 -index 0000000000000000000000000000000000000000..de182b9473963b95085fa612f70884a56765ae43 ---- /dev/null -+++ b/src/main/java/org/galemc/gale/concurrent/AsyncExecutor.java -@@ -0,0 +1,103 @@ -+// Gale - centralized async execution -+ -+package org.galemc.gale.concurrent; -+ -+import com.google.common.util.concurrent.ThreadFactoryBuilder; -+import net.minecraft.Util; -+import org.galemc.gale.util.CPUCoresEstimation; -+ -+import java.util.concurrent.BlockingQueue; -+import java.util.concurrent.LinkedBlockingQueue; -+import java.util.concurrent.ThreadPoolExecutor; -+import java.util.concurrent.TimeUnit; -+import java.util.concurrent.locks.Condition; -+import java.util.concurrent.locks.ReentrantLock; -+ -+/** -+ * An executor for tasks that can run asynchronously. This executor uses a fixed thread pool, and as such -+ * is not appropriate for tasks that block. -+ *
-+ * It can be paused when all CPU cores may be needed for something else. -+ * -+ * @author Martijn Muijsers -+ */ -+public final class AsyncExecutor extends ThreadPoolExecutor { -+ -+ /** -+ * The fixed number of threads that will be used by this {@link AsyncExecutor}. -+ *
-+ * By default, we do not use two cores, so that there is always a core for the main thread that we do not use, -+ * and another core that we do not use to run other important threads such as garbage collection on. -+ *
-+ * This value is at least 1. -+ */ -+ public static final int parallelism; -+ static { -+ int parallelismByEnvironmentVariable = Integer.getInteger("gale.threads.async", -1); -+ parallelism = Math.max(1, parallelismByEnvironmentVariable > 0 ? parallelismByEnvironmentVariable : CPUCoresEstimation.get() - 2); -+ } -+ -+ /** -+ * The queue of tasks in the {@link AsyncExecutor} singleton instance. -+ * This queue can be accessed externally to steal work from the executor. -+ */ -+ public static final BlockingQueue queue = new LinkedBlockingQueue<>(); -+ -+ /** -+ * Singleton {@link AsyncExecutor} instance. -+ */ -+ public static final AsyncExecutor instance = new AsyncExecutor(); -+ -+ private static volatile boolean isPaused = false; -+ private static final ReentrantLock pauseLock = new ReentrantLock(); -+ private static final Condition pauseCondition = pauseLock.newCondition(); -+ -+ private AsyncExecutor() { -+ super(parallelism, parallelism, 0L, TimeUnit.MILLISECONDS, queue, new ThreadFactoryBuilder() -+ .setNameFormat("Async Executor Thread - %1$d") -+ .setPriority(Thread.NORM_PRIORITY - 1) // Deprioritize over main -+ .setUncaughtExceptionHandler(Util::onThreadException) -+ .build()); -+ } -+ -+ @Override -+ protected void beforeExecute(Thread t, Runnable r) { -+ super.beforeExecute(t, r); -+ pauseLock.lock(); -+ try { -+ while (isPaused) pauseCondition.await(); -+ } catch (InterruptedException ie) { -+ t.interrupt(); -+ } finally { -+ pauseLock.unlock(); -+ } -+ } -+ -+ /** -+ * Pauses the {@link AsyncExecutor} from starting to run any new task until {@link #resume()} is called. -+ *
-+ * This does not affect execution of tasks that are already being performed when this method is called. -+ */ -+ public static void pause() { -+ pauseLock.lock(); -+ try { -+ isPaused = true; -+ } finally { -+ pauseLock.unlock(); -+ } -+ } -+ -+ /** -+ * Resumes the {@link AsyncExecutor} singleton instance after it has been paused using {@link #pause()}. -+ */ -+ public static void resume() { -+ pauseLock.lock(); -+ try { -+ isPaused = false; -+ pauseCondition.signalAll(); -+ } finally { -+ pauseLock.unlock(); -+ } -+ } -+ -+} diff --git a/patches/server/0111-Mutex-utility.patch b/patches/server/0111-Mutex-utility.patch new file mode 100644 index 0000000..180f0cc --- /dev/null +++ b/patches/server/0111-Mutex-utility.patch @@ -0,0 +1,129 @@ +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..76e4d7b2f242218df7e853b416a69d62707357e8 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/concurrent/Mutex.java +@@ -0,0 +1,76 @@ ++// Gale - mutex utility ++ ++package org.galemc.gale.concurrent; ++ ++import org.galemc.gale.executor.annotation.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. ++ * ++ * @author Martijn Muijsers ++ */ ++@AnyThreadSafe ++public interface Mutex extends Lock { ++ ++ 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(); ++ } ++ ++ /** ++ * 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 Lock createLock() { ++ return create(); ++ } ++ ++} +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..f8e3151e6ba1ef0850f50c836962b33f088de375 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java +@@ -0,0 +1,33 @@ ++// Gale - mutex utility ++ ++package org.galemc.gale.concurrent; ++ ++import org.galemc.gale.executor.annotation.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 ++ */ ++@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"); ++ } ++ ++} diff --git a/patches/server/0112-Paired-lock-and-condition-utility.patch b/patches/server/0112-Paired-lock-and-condition-utility.patch new file mode 100644 index 0000000..d88a556 --- /dev/null +++ b/patches/server/0112-Paired-lock-and-condition-utility.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martijn Muijsers +Date: Fri, 2 Dec 2022 20:31:06 +0100 +Subject: [PATCH] Paired lock and condition 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/LockAndCondition.java b/src/main/java/org/galemc/gale/concurrent/LockAndCondition.java +new file mode 100644 +index 0000000000000000000000000000000000000000..73fd8ca0bd1168862a03d9bdcae93d62895e8c1f +--- /dev/null ++++ b/src/main/java/org/galemc/gale/concurrent/LockAndCondition.java +@@ -0,0 +1,28 @@ ++// Gale - paired lock and condition utility ++ ++package org.galemc.gale.concurrent; ++ ++import java.util.concurrent.locks.Condition; ++import java.util.concurrent.locks.Lock; ++ ++/** ++ * A utility class that stores a {@link Condition} with its {@link Lock}, that can be passed around and used instead ++ * of using an {@link Object} monitor, which does not support speculative locking. ++ * ++ * @author Martijn Muijsers ++ */ ++public class LockAndCondition { ++ ++ public final Lock lock; ++ public final Condition condition; ++ ++ public LockAndCondition(Lock lock) { ++ this(lock, lock.newCondition()); ++ } ++ ++ public LockAndCondition(Lock lock, Condition condition) { ++ this.lock = lock; ++ this.condition = condition; ++ } ++ ++} diff --git a/patches/server/0112-Remove-Paper-async-executor.patch b/patches/server/0112-Remove-Paper-async-executor.patch deleted file mode 100644 index cccce3e..0000000 --- a/patches/server/0112-Remove-Paper-async-executor.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers -Date: Tue, 29 Nov 2022 13:10:20 +0100 -Subject: [PATCH] Remove Paper async executor - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) - -diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java -index 1e812c4f5a8d54293084de29e8731e0b8ddcc0ae..06507695eb53067b0da9cb66e4d443f21cb6f070 100644 ---- a/src/main/java/io/papermc/paper/util/MCUtil.java -+++ b/src/main/java/io/papermc/paper/util/MCUtil.java -@@ -34,6 +34,7 @@ import org.bukkit.Location; - import org.bukkit.block.BlockFace; - import org.bukkit.craftbukkit.CraftWorld; - import org.bukkit.craftbukkit.util.Waitable; -+import org.galemc.gale.concurrent.AsyncExecutor; - import org.spigotmc.AsyncCatcher; - - import javax.annotation.Nonnull; -@@ -45,6 +46,7 @@ import java.util.Queue; - import java.util.Set; - import java.util.concurrent.CompletableFuture; - import java.util.concurrent.ExecutionException; -+import java.util.concurrent.Executor; - import java.util.concurrent.LinkedBlockingQueue; - import java.util.concurrent.ThreadPoolExecutor; - import java.util.concurrent.TimeUnit; -@@ -55,14 +57,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 = AsyncExecutor.instance; // Gale - centralized async execution - 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 9045ae692d77f986542940334dd15edef6b2a9f4..b124ef503363e64774817f214d97660e331e5291 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -148,6 +148,7 @@ import net.minecraft.world.level.storage.loot.PredicateManager; - import net.minecraft.world.phys.Vec2; - import net.minecraft.world.phys.Vec3; - import org.apache.commons.lang3.Validate; -+import org.galemc.gale.concurrent.AsyncExecutor; - import org.galemc.gale.concurrent.MinecraftServerBlockableEventLoop; - import org.galemc.gale.configuration.GaleConfigurations; - import org.galemc.gale.configuration.GaleGlobalConfiguration; -@@ -980,8 +981,10 @@ 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 -+ // Gale start - centralized async execution - remove Paper async executor -+ AsyncExecutor.instance.shutdown(); // Paper -+ try { AsyncExecutor.instance.awaitTermination(30, java.util.concurrent.TimeUnit.SECONDS); // Paper -+ // Gale end - centralized async execution - remove Paper async executor - } catch (java.lang.InterruptedException ignored) {} // Paper - if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) { - MinecraftServer.LOGGER.info("Saving usercache.json"); diff --git a/patches/server/0113-Remove-Paper-cleaner-executor.patch b/patches/server/0113-Remove-Paper-cleaner-executor.patch deleted file mode 100644 index 8325142..0000000 --- a/patches/server/0113-Remove-Paper-cleaner-executor.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers -Date: Tue, 29 Nov 2022 13:11:58 +0100 -Subject: [PATCH] Remove Paper cleaner executor - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) - -diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java -index 06507695eb53067b0da9cb66e4d443f21cb6f070..d9b1c85b4e36055b58c71748b005adf08c61f72f 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.Supplier; - - public final class MCUtil { - public static final Executor asyncExecutor = AsyncExecutor.instance; // Gale - centralized async execution - 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 = AsyncExecutor.instance; // Gale - centralized async execution - remove Paper cleaner executor - - public static final long INVALID_CHUNK_KEY = getCoordinateKey(Integer.MAX_VALUE, Integer.MAX_VALUE); - diff --git a/patches/server/0113-Unterminable-executor-utility.patch b/patches/server/0113-Unterminable-executor-utility.patch new file mode 100644 index 0000000..f54885f --- /dev/null +++ b/patches/server/0113-Unterminable-executor-utility.patch @@ -0,0 +1,64 @@ +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..0a8fe0a883d2d7f5fd9a454006ded849ceaa9c0a +--- /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.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/0114-FIFO-concurrent-queue-utility.patch b/patches/server/0114-FIFO-concurrent-queue-utility.patch new file mode 100644 index 0000000..e492ec9 --- /dev/null +++ b/patches/server/0114-FIFO-concurrent-queue-utility.patch @@ -0,0 +1,147 @@ +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..010035d51ceba9ec59821a95386c54a324e715b5 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/collection/FIFOConcurrentLinkedQueue.java +@@ -0,0 +1,133 @@ ++// Gale - FIFO concurrent queue utility ++ ++package org.galemc.gale.collection; ++ ++import org.galemc.gale.executor.annotation.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/0114-Remove-background-executor.patch b/patches/server/0114-Remove-background-executor.patch deleted file mode 100644 index 8d91955..0000000 --- a/patches/server/0114-Remove-background-executor.patch +++ /dev/null @@ -1,78 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers -Date: Tue, 29 Nov 2022 14:21:44 +0100 -Subject: [PATCH] Remove background executor - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) - -diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java -index 6b7943e8348b0a41ca69fb56ccfd5f1c1484eb07..e14245a77b40fca4bacf82295ad390339aba08a9 100644 ---- a/src/main/java/net/minecraft/Util.java -+++ b/src/main/java/net/minecraft/Util.java -@@ -72,6 +72,7 @@ import net.minecraft.util.RandomSource; - import net.minecraft.util.TimeSource; - import net.minecraft.util.datafix.DataFixers; - import net.minecraft.world.level.block.state.properties.Property; -+import org.galemc.gale.concurrent.AsyncExecutor; - import org.slf4j.Logger; - - public class Util { -@@ -80,7 +81,7 @@ public class Util { - private static final String MAX_THREADS_SYSTEM_PROPERTY = "max.bg.threads"; - private static final AtomicInteger WORKER_COUNT = new AtomicInteger(1); - private static final ExecutorService BOOTSTRAP_EXECUTOR = makeExecutor("Bootstrap", -2); // Paper - add -2 priority -- private static final ExecutorService BACKGROUND_EXECUTOR = makeExecutor("Main", -1); // Paper - add -1 priority -+ private static final ExecutorService BACKGROUND_EXECUTOR = AsyncExecutor.instance; // Gale - centralized async execution - 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() { - -@@ -219,7 +220,6 @@ public class Util { - } - - public static void shutdownExecutors() { -- shutdownExecutor(BACKGROUND_EXECUTOR); - shutdownExecutor(IO_POOL); - } - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index b124ef503363e64774817f214d97660e331e5291..537ce3fc1cae549e3cf92e8822d00cc572c50ac6 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -981,11 +981,6 @@ public abstract class MinecraftServer extends MinecraftServerBlockableEventLoop - MinecraftServer.LOGGER.error("Failed to unlock level {}", this.storageSource.getLevelId(), ioexception1); - } - // Spigot start -- // Gale start - centralized async execution - remove Paper async executor -- AsyncExecutor.instance.shutdown(); // Paper -- try { AsyncExecutor.instance.awaitTermination(30, java.util.concurrent.TimeUnit.SECONDS); // Paper -- // Gale end - centralized async execution - remove Paper async executor -- } catch (java.lang.InterruptedException ignored) {} // Paper - if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) { - MinecraftServer.LOGGER.info("Saving usercache.json"); - this.getProfileCache().save(false); // Paper -@@ -995,6 +990,13 @@ 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 - centralized async execution - remove Paper async executor, remove background executor -+ AsyncExecutor.instance.shutdown(); // Paper -+ try { -+ AsyncExecutor.instance.awaitTermination(30, java.util.concurrent.TimeUnit.SECONDS); // Paper -+ // Gale end - centralized async execution - remove Paper async executor -+ } catch (java.lang.InterruptedException ignored) {} // Paper -+ // Gale start - centralized async execution - remove background executor - Util.shutdownExecutors(); // Paper - LOGGER.info("Closing Server"); - try { -diff --git a/src/main/java/org/galemc/gale/concurrent/AsyncExecutor.java b/src/main/java/org/galemc/gale/concurrent/AsyncExecutor.java -index de182b9473963b95085fa612f70884a56765ae43..fff4549d86e672dc7b9959ac5dd51fd04d4d62c3 100644 ---- a/src/main/java/org/galemc/gale/concurrent/AsyncExecutor.java -+++ b/src/main/java/org/galemc/gale/concurrent/AsyncExecutor.java -@@ -34,6 +34,7 @@ public final class AsyncExecutor extends ThreadPoolExecutor { - public static final int parallelism; - static { - int parallelismByEnvironmentVariable = Integer.getInteger("gale.threads.async", -1); -+ parallelismByEnvironmentVariable = Math.max(parallelismByEnvironmentVariable, Integer.getInteger("Paper.WorkerThreadCount", -1)); // Gale - centralized async execution - remove background executor - parallelism = Math.max(1, parallelismByEnvironmentVariable > 0 ? parallelismByEnvironmentVariable : CPUCoresEstimation.get() - 2); - } - diff --git a/patches/server/0115-Base-thread-pool.patch b/patches/server/0115-Base-thread-pool.patch new file mode 100644 index 0000000..c2f849c --- /dev/null +++ b/patches/server/0115-Base-thread-pool.patch @@ -0,0 +1,5455 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martijn Muijsers +Date: Fri, 2 Dec 2022 11:43:51 +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/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadedTaskQueue.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/standard/PrioritisedThreadedTaskQueue.java +index b71404be2c82f7db35272b367af861e94d6c73d3..22089204d13366bb265305ef14d08b0468ff5055 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.AllLevelsScheduledMainThreadChunkTaskQueue; ++ + 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; ++ AllLevelsScheduledMainThreadChunkTaskQueue.signalReason.signalAnother(); ++ } ++ // 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/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java +index dabd93c35bdbac6a8b668a82d5f3d4173a1baa4a..f22e8930e780b4bf11052504c996b8852d0c5304 100644 +--- a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java ++++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java +@@ -21,6 +21,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.ScheduledMainThreadTaskQueues; + + import java.util.*; + import java.util.concurrent.Executor; +@@ -180,7 +181,7 @@ public final class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockCo + + if (!Bukkit.isPrimaryThread()) { + // Plugins? +- MinecraftServer.getServer().scheduleOnMain(() -> modifyBlocks(chunkPacket, chunkPacketInfo)); ++ ScheduledMainThreadTaskQueues.add(() -> modifyBlocks(chunkPacket, chunkPacketInfo), ScheduledMainThreadTaskQueues.ANTI_XRAY_MODIFY_BLOCKS_TASK_MAX_DELAY); // Gale - base thread pool + return; + } + +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..d89b8ea24844185661ac93dd4cc9f26696e967cb 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 end - base thread pool - private -> public, count delayed tasks + + final ReentrantLock schedulingLock = new ReentrantLock(); + public final ChunkHolderManager chunkHolderManager; +diff --git a/src/main/java/io/papermc/paper/configuration/Configurations.java b/src/main/java/io/papermc/paper/configuration/Configurations.java +index cf6d50218769e3fecd12dbde70a03b5042feddf4..9d8ee965f7dcd0f416b7aa8368e34b911edef6b0 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 ceef4eed87363298816426dfc19f3207be1af682..ec76ed64fe3082810f4d790c5b73e731d5d524e9 100644 +--- a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java ++++ b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java +@@ -285,7 +285,7 @@ public class PaperConfigurations extends Configurations(), +- new ThreadFactoryBuilder() +- .setNameFormat("Paper Async Task Handler Thread - %1$d") +- .setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(MinecraftServer.LOGGER)) +- .build() +- ); +- 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 asyncExecutor = BaseTaskQueues.scheduledAsync.yieldingExecutor; // Gale - base thread pool - remove Paper async executor ++ public static final Executor cleanerExecutor = BaseTaskQueues.scheduledAsync.yieldingExecutor; // 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/io/papermc/paper/util/TickThread.java b/src/main/java/io/papermc/paper/util/TickThread.java +index fc57850b80303fcade89ca95794f63910404a407..8f95f98eef90d2618ffc051885bfbd3dc121f561 100644 +--- a/src/main/java/io/papermc/paper/util/TickThread.java ++++ b/src/main/java/io/papermc/paper/util/TickThread.java +@@ -4,9 +4,12 @@ import net.minecraft.server.MinecraftServer; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.world.entity.Entity; + import org.bukkit.Bukkit; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.MainThreadClaim; ++ + import java.util.concurrent.atomic.AtomicInteger; + +-public class TickThread extends Thread { ++public class TickThread extends BaseThread { // Gale - base thread pool + + public static final boolean STRICT_THREAD_CHECKS = Boolean.getBoolean("paper.strict-thread-checks"); + +@@ -65,23 +68,19 @@ public class TickThread extends Thread { + } + + private TickThread(final Runnable run, final String name, final int id) { +- super(run, name); ++ super(run, name, 0); // Gale - base thread pool + this.id = id; + } + +- public static TickThread getCurrentTickThread() { +- return (TickThread)Thread.currentThread(); +- } +- + public static boolean isTickThread() { +- return Thread.currentThread() instanceof TickThread; ++ return MainThreadClaim.isCurrentThreadMainThreadAndNotClaimable(); // Gale - base thread pool + } + + public static boolean isTickThreadFor(final ServerLevel world, final int chunkX, final int chunkZ) { +- return Thread.currentThread() instanceof TickThread; ++ return MainThreadClaim.isCurrentThreadMainThreadAndNotClaimable(); // Gale - base thread pool + } + + public static boolean isTickThreadFor(final Entity entity) { +- return Thread.currentThread() instanceof TickThread; ++ return MainThreadClaim.isCurrentThreadMainThreadAndNotClaimable(); // Gale - base thread pool + } + } +diff --git a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java +index 95cac7edae8ac64811fc6a2f6b97dd4a0fceb0b0..a376259202b4a16c67db4d3ef071e0b395aca524 100644 +--- a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java ++++ b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java +@@ -7,25 +7,21 @@ 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; + import java.util.Optional; + import java.util.concurrent.ExecutorService; +-import java.util.concurrent.Executors; +-import java.util.concurrent.ThreadFactory; +-import java.util.concurrent.atomic.AtomicInteger; + import java.util.concurrent.atomic.AtomicLong; + import java.util.function.Supplier; + +@@ -46,6 +42,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(); + +@@ -61,6 +61,7 @@ public class ThreadedWorldUpgrader { + return ret; + } + }); ++ */ // Gale - base thread pool - remove world upgrade executors + this.dataFixer = dataFixer; + this.generatorKey = generatorKey; + this.removeCaches = removeCaches; +diff --git a/src/main/java/me/titaniumtown/ArrayConstants.java b/src/main/java/me/titaniumtown/ArrayConstants.java +index ceec23a85aae625fbbe2db95c8e9c83fb9f9767c..fdb9eefdcbd4abf1936761136077c3d10ef5e594 100644 +--- a/src/main/java/me/titaniumtown/ArrayConstants.java ++++ b/src/main/java/me/titaniumtown/ArrayConstants.java +@@ -2,6 +2,8 @@ + + package me.titaniumtown; + ++import net.minecraft.server.level.ServerLevel; ++ + public final class ArrayConstants { + + private ArrayConstants() {} +@@ -14,5 +16,6 @@ public final class ArrayConstants { + public static final long[] emptyLongArray = new long[0]; + public static final org.bukkit.entity.Entity[] emptyBukkitEntityArray = new org.bukkit.entity.Entity[0]; + public static final net.minecraft.world.entity.Entity[] emptyEntityArray = new net.minecraft.world.entity.Entity[0]; ++ public static final ServerLevel[] emptyServerLevelArray = new ServerLevel[0]; // Gale - base thread pool + + } +diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java +index 6b7943e8348b0a41ca69fb56ccfd5f1c1484eb07..c2cab5f6be64e7a7adf03bb004709d81cf0eee42 100644 +--- a/src/main/java/net/minecraft/Util.java ++++ b/src/main/java/net/minecraft/Util.java +@@ -27,9 +27,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.CompletionException; + 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,11 +62,11 @@ 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.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 { +@@ -79,8 +74,8 @@ 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 BOOTSTRAP_EXECUTOR = makeExecutor("Bootstrap", -2); // Paper - add -2 priority +- 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 ++ private static final ExecutorService BOOTSTRAP_EXECUTOR = BACKGROUND_EXECUTOR; // Gale - Patina - remove bootstrap 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() { + +@@ -219,7 +214,6 @@ public class Util { + } + + public static void shutdownExecutors() { +- shutdownExecutor(BACKGROUND_EXECUTOR); + shutdownExecutor(IO_POOL); + } + +diff --git a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java +index 40812e6518b8aacfbd2d8cd65a407d00bb19e991..eb1209b760890e6ede8ee149a6debfe83336ee10 100644 +--- a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java ++++ b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java +@@ -144,10 +144,7 @@ public class EntitySelector { + if (this.isWorldLimited()) { + this.addEntities(list, source.getLevel(), vec3d, predicate); + } else { +- Iterator iterator1 = source.getServer().getAllLevels().iterator(); +- +- while (iterator1.hasNext()) { +- ServerLevel worldserver1 = (ServerLevel) iterator1.next(); ++ for (ServerLevel worldserver1 : source.getServer().getAllLevelsArray()) { // Gale - base thread pool - optimize server levels + + this.addEntities(list, worldserver1, vec3d, predicate); + } +diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java +index 8bc0cb9ad5bb4e76d962ff54305e2c08e279a17b..405ffeb584d60b3677ae5d8fb79e084635d2dea3 100644 +--- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java ++++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java +@@ -2,6 +2,7 @@ package net.minecraft.network.protocol; + + import com.mojang.logging.LogUtils; + import net.minecraft.network.PacketListener; ++import org.galemc.gale.executor.AbstractBlockableEventLoop; + import org.slf4j.Logger; + + // CraftBukkit start +@@ -9,7 +10,6 @@ import net.minecraft.server.MinecraftServer; + import net.minecraft.server.RunningOnDifferentThreadException; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.server.network.ServerGamePacketListenerImpl; +-import net.minecraft.util.thread.BlockableEventLoop; + + public class PacketUtils { + +@@ -36,10 +36,10 @@ public class PacketUtils { + public PacketUtils() {} + + public static 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.executeIfPossible(() -> { + 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 d8f8d2495c1e2e3f194485d16ea587d26cc3a23d..ae423cc7ef2d19d7d5f89ac9cb2962055acda40e 100644 +--- a/src/main/java/net/minecraft/server/Main.java ++++ b/src/main/java/net/minecraft/server/Main.java +@@ -55,6 +55,7 @@ import net.minecraft.world.level.levelgen.presets.WorldPresets; + import net.minecraft.world.level.storage.LevelResource; + import net.minecraft.world.level.storage.LevelStorageSource; + import net.minecraft.world.level.storage.LevelSummary; ++import org.galemc.gale.executor.thread.SecondaryThreadPool; + import org.slf4j.Logger; + + // CraftBukkit start +@@ -224,6 +225,8 @@ public class Main { + AtomicReference> ops = new AtomicReference<>(); + // CraftBukkit end + ++ SecondaryThreadPool.startSecondaryThreads(); // Gale - base thread pool ++ + WorldStem worldstem; + + try { +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 0b3b1400e9546060b4ee35236741670aaa226820..8c9738bf077ff37aae269959d071f640e2b1deab 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -10,6 +10,7 @@ import com.mojang.authlib.GameProfileRepository; + import com.mojang.authlib.minecraft.MinecraftSessionService; + import com.mojang.datafixers.DataFixer; + import com.mojang.logging.LogUtils; ++import io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler; + import it.unimi.dsi.fastutil.longs.LongIterator; + import java.awt.image.BufferedImage; + import java.io.BufferedWriter; +@@ -40,7 +41,7 @@ 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.AtomicInteger; + import java.util.concurrent.atomic.AtomicReference; + import java.util.function.BooleanSupplier; + import java.util.function.Consumer; +@@ -115,7 +116,6 @@ 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; +@@ -150,8 +150,17 @@ import net.minecraft.world.level.storage.loot.PredicateManager; + import net.minecraft.world.phys.Vec2; + import net.minecraft.world.phys.Vec3; + import org.apache.commons.lang3.Validate; ++import org.bukkit.Bukkit; ++import org.galemc.gale.executor.MinecraftServerBlockableEventLoop; + import org.galemc.gale.configuration.GaleConfigurations; + import org.galemc.gale.configuration.GaleGlobalConfiguration; ++import org.galemc.gale.executor.queue.BaseTaskQueues; ++import org.galemc.gale.executor.queue.ScheduledMainThreadTaskQueues; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.MainThreadClaim; ++import org.galemc.gale.executor.thread.SecondaryThreadPool; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++import org.jetbrains.annotations.NotNull; + import org.slf4j.Logger; + + // CraftBukkit start +@@ -174,9 +183,12 @@ import org.bukkit.event.server.ServerLoadEvent; + + 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 + +- private static MinecraftServer SERVER; // Paper ++ // Gale start - base thread pool ++ public static MinecraftServer SERVER; // Paper // Gale - base thread pool - private -> public ++ public static boolean canPollAsyncTasksOnOriginalServerThread = true; ++ // Gale end - base thread pool + public static final Logger LOGGER = LogUtils.getLogger(); + public static final String VANILLA_BRAND = "vanilla"; + private static final float AVERAGE_TICK_TIME_SMOOTHING = 0.8F; +@@ -214,6 +226,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop, 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 +@@ -243,10 +259,52 @@ 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; +@@ -292,9 +350,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 ++ BaseThread thread = new io.papermc.paper.util.TickThread(() -> { // Paper - rewrite chunk system // Gale - base thread pool + ((MinecraftServer) atomicreference.get()).runServer(); + }, "Server thread"); + +@@ -313,9 +371,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop registryreadops, Thread thread, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PackRepository resourcepackrepository, WorldStem worldstem, Proxy proxy, DataFixer datafixer, Services services, ChunkProgressListenerFactory worldloadlistenerfactory) { +- super("Server"); ++ public MinecraftServer(OptionSet options, DataPackConfig datapackconfiguration, DynamicOps registryreadops, BaseThread thread, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PackRepository resourcepackrepository, WorldStem worldstem, Proxy proxy, DataFixer datafixer, Services services, ChunkProgressListenerFactory worldloadlistenerfactory) { // Gale - base thread pool ++ super(); // Gale - base thread pool + SERVER = this; // Paper - better singleton ++ canPollAsyncTasksOnOriginalServerThread = false; + this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; + this.profiler = this.metricsRecorder.getProfiler(); + this.onMetricsRecordingStopped = (methodprofilerresults) -> { +@@ -353,7 +412,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop public + public volatile boolean hasFullyShutdown = false; // Paper + private boolean hasLoggedStop = false; // Paper + private final Object stopLock = new Object(); +@@ -922,8 +981,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop !BaseTaskQueues.scheduledAsync.hasTasks(), null, System.nanoTime() + 30_000_000_000L); // Paper ++ LOGGER.info("Shutting down IO executor..."); ++ // Gale end - base thread pool - remove Paper async executor ++ // Gale end - base thread pool - remove background executor + Util.shutdownExecutors(); // Paper + LOGGER.info("Closing Server"); + try { +@@ -1026,7 +1087,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop public + // Paper start + if (this.forceTicks) { + return true; +@@ -1273,13 +1373,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { + return !this.canSleepForTickNoOversleep(); // Paper - move oversleep into full server tick +- }); ++ // Gale start - base thread pool ++ }, 1_000_000L * this.nextTickTime - 200_000L); ++ isInSpareTime = 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 +- this.getProfiler().incrementCounter("runTask"); +- super.doRunTask(ticktask); +- } +- + private void updateStatusIcon(ServerStatus metadata) { + Optional optional = Optional.of(this.getFile("server-icon.png")).filter(File::isFile); + +@@ -1399,14 +1453,19 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { + return !this.canOversleep(); +- }); ++ // Gale start - base thread pool ++ }, 1_000_000L * this.nextTickTime - 200_000L); ++ 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; ++ ScheduledMainThreadTaskQueues.shiftTasksForNextTick(); // Gale - base thread pool + this.tickChildren(shouldKeepTicking); + if (i - this.lastServerStatus >= 5000000000L) { + this.lastServerStatus = i; +@@ -1442,7 +1501,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); + } +@@ -1455,7 +1514,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper + worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.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 +@@ -1609,7 +1666,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(this.levelArray); ++ 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) { +@@ -1638,6 +1707,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(this.levelArray); ++ 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 + +@@ -1645,8 +1722,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() { +@@ -1775,10 +1858,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop registryreadops, Thread thread, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PackRepository resourcepackrepository, WorldStem worldstem, DedicatedServerSettings dedicatedserversettings, DataFixer datafixer, Services services, ChunkProgressListenerFactory worldloadlistenerfactory) { ++ public DedicatedServer(joptsimple.OptionSet options, DataPackConfig datapackconfiguration, DynamicOps registryreadops, BaseThread thread, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PackRepository resourcepackrepository, WorldStem worldstem, DedicatedServerSettings dedicatedserversettings, DataFixer datafixer, Services services, ChunkProgressListenerFactory worldloadlistenerfactory) { // Gale - base thread pool + super(options, datapackconfiguration, registryreadops, 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 4654995f9982e77abe4b825b32312c2913671cd4..f833c02b92f995d3f9a09aa6a6594f8ceb800c6e 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -11,7 +11,6 @@ import java.util.Collections; + import java.util.Iterator; + import java.util.List; + import java.util.Objects; +-import java.util.Optional; + import java.util.concurrent.CompletableFuture; + import java.util.concurrent.Executor; + import java.util.function.BooleanSupplier; +@@ -22,6 +21,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.profiling.ProfilerFiller; +@@ -48,6 +48,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.queue.AllLevelsScheduledChunkCacheTaskQueue; + + public class ServerChunkCache extends ChunkSource { + +@@ -994,6 +995,14 @@ public class ServerChunkCache extends ChunkSource { + super.doRunTask(task); + } + ++ // Gale start - base thread pool ++ @Override ++ public void tell(Runnable runnable) { ++ super.tell(runnable); ++ MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks = true; ++ AllLevelsScheduledChunkCacheTaskQueue.signalReason.signalAnother(); ++ } ++ + @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 023cd8d948ff7360ac8348b980473ef119b41225..4869e23d7070649e216dcdc64687da61b670355a 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -22,6 +22,7 @@ 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; +@@ -155,6 +156,9 @@ 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.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.MainThreadOnly; + import org.slf4j.Logger; + import org.bukkit.Bukkit; + import org.bukkit.Location; +@@ -188,6 +192,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) @MainThreadOnly(Access.WRITE) ++ public volatile int serverLevelArrayIndex; ++ // Gale end - base thread pool + private final MinecraftServer server; + public final PrimaryLevelData serverLevelData; // CraftBukkit - type + final EntityTickList entityTickList; +@@ -2609,7 +2617,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 c171c272d5fcf0900514e18eafaa1b5ee019c74d..b9b9b14f5235d0e07feaa1dfedf254fa43880d6e 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -187,6 +187,8 @@ import net.minecraft.world.phys.shapes.Shapes; + import net.minecraft.world.phys.shapes.VoxelShape; + 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.ScheduledMainThreadTaskQueues; + import org.slf4j.Logger; + + // CraftBukkit start +@@ -556,7 +558,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + + Objects.requireNonNull(this.connection); + // CraftBukkit - Don't wait +- minecraftserver.scheduleOnMain(networkmanager::handleDisconnection); // Paper ++ ScheduledMainThreadTaskQueues.add(networkmanager::handleDisconnection, ScheduledMainThreadTaskQueues.HANDLE_DISCONNECT_TASK_MAX_DELAY); // Paper // Gale - base thread pool + } + + private CompletableFuture filterTextPacket(T text, BiFunction> filterer) { +@@ -887,21 +889,20 @@ 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) { + // 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 ++ ScheduledMainThreadTaskQueues.add(() -> this.disconnect(Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM), ScheduledMainThreadTaskQueues.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 ++ ScheduledMainThreadTaskQueues.add(() -> this.disconnect(Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM), ScheduledMainThreadTaskQueues.KICK_FOR_COMMAND_PACKET_SPAM_TASK_MAX_DELAY); // Paper - kick event cause // Gale - JettPack - reduce array allocations // Gale - base thread pool + return; + } + // Paper end +@@ -926,7 +927,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + if (!event.isHandled()) { + if (!event.isCancelled()) { + +- this.server.scheduleOnMain(() -> { // This needs to be on main ++ ScheduledMainThreadTaskQueues.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) -> { +@@ -937,7 +938,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestEvent.getSuggestions())); + // Paper end - Brigadier API + }); +- }); ++ }, ScheduledMainThreadTaskQueues.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()); +@@ -1246,7 +1247,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 ++ ScheduledMainThreadTaskQueues.add(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION), ScheduledMainThreadTaskQueues.KICK_FOR_BOOK_TOO_LARGE_PACKET_TASK_MAX_DELAY); // Paper - kick event cause // Gale - base thread pool + return; + } + byteTotal += byteLength; +@@ -1269,14 +1270,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 ++ ScheduledMainThreadTaskQueues.add(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION), ScheduledMainThreadTaskQueues.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 ++ ScheduledMainThreadTaskQueues.add(() -> this.disconnect("Book edited too quickly!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION), ScheduledMainThreadTaskQueues.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; +@@ -2077,10 +2078,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) { +@@ -2228,9 +2226,9 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + } + // CraftBukkit end + if (ServerGamePacketListenerImpl.isChatMessageIllegal(packet.message())) { +- this.server.scheduleOnMain(() -> { // Paper - push to main for event firing ++ ScheduledMainThreadTaskQueues.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 ++ }, ScheduledMainThreadTaskQueues.KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY); // Paper - push to main for event firing // Gale - base thread pool + } else { + if (this.tryHandleChat(packet.message(), packet.timeStamp(), packet.lastSeenMessages())) { + // this.server.submit(() -> { // CraftBukkit - async chat +@@ -2258,9 +2256,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 ++ ScheduledMainThreadTaskQueues.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 ++ }, ScheduledMainThreadTaskQueues.KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY); // Paper - push to main for event firing // Gale - base thread pool + } else { + if (this.tryHandleChat(packet.command(), packet.timeStamp(), packet.lastSeenMessages())) { + this.server.submit(() -> { +@@ -2357,9 +2355,9 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + private boolean tryHandleChat(String message, Instant timestamp, LastSeenMessages.Update acknowledgment) { + if (!this.updateChatOrder(timestamp)) { + ServerGamePacketListenerImpl.LOGGER.warn("{} sent out-of-order chat: '{}': {} > {}", this.player.getName().getString(), message, this.lastChatTimeStamp.get().getEpochSecond(), timestamp.getEpochSecond()); // Paper +- this.server.scheduleOnMain(() -> { // Paper - push to main ++ ScheduledMainThreadTaskQueues.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 +- }); // Paper - push to main ++ }, ScheduledMainThreadTaskQueues.KICK_FOR_OUT_OF_ORDER_CHAT_PACKET_TASK_MAX_DELAY); // Paper - push to main // Gale - base thread pool + return false; + } else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales + this.send(new ClientboundSystemChatPacket(Component.translatable("chat.disabled.options").withStyle(ChatFormatting.RED), false)); +@@ -3420,7 +3418,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 ++ ScheduledMainThreadTaskQueues.add(() -> this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam", ArrayConstants.emptyObjectArray), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM), ScheduledMainThreadTaskQueues.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/network/TextFilterClient.java b/src/main/java/net/minecraft/server/network/TextFilterClient.java +index 2393b6a5f3f12c2b17b172ee8ca42ead218e2a10..ff2d2366966b00436a350ec73819930248a0fc4f 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/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index a60db92d8c6afab40e12b3ac28241beac06bcf63..e28f69ca50b513faa7eb656992be14ab8ee50291 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.Iterator; +@@ -101,10 +100,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.ScheduledMainThreadTaskQueues; + 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; +@@ -306,7 +305,7 @@ public abstract class PlayerList { + worldserver1, chunkX, chunkZ, net.minecraft.server.level.ChunkHolder.FullChunkStatus.ENTITY_TICKING, true, + ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHEST, + (chunk) -> { +- MinecraftServer.getServer().scheduleOnMain(() -> { ++ ScheduledMainThreadTaskQueues.add(() -> { // Gale - base thread pool + try { + if (!playerconnection.connection.isConnected()) { + return; +@@ -319,7 +318,7 @@ public abstract class PlayerList { + } finally { + finalWorldserver.pendingLogin.remove(player); + } +- }); ++ }, ScheduledMainThreadTaskQueues.POST_CHUNK_LOAD_JOIN_TASK_MAX_DELAY); // Gale - base thread pool + } + ); + } +@@ -1578,10 +1577,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); +@@ -1593,10 +1590,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 ea23e771ab2b77e8001d0eaaf834423353ef70c2..4fc0deca1ba4da302f40cbf8847420d0042b44da 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +@@ -97,7 +97,7 @@ public abstract class Projectile extends Entity { + 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 69bde99acff7bdae9af7cfe60e2221675a68b858..eb61050067c0f182ef50f4c382add3ea03019701 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -969,7 +969,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)) + +@@ -1153,7 +1153,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"); + +@@ -2498,7 +2498,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 55d83a9a691d11c9408d2c3260c3e77dfb51b97e..89edfcf25e0359905e26181262b9ea5d7b99c56c 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; +@@ -21,7 +20,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; +@@ -43,7 +41,6 @@ import net.minecraft.server.level.ServerPlayer; + import net.minecraft.server.level.Ticket; + import net.minecraft.server.level.TicketType; + import net.minecraft.sounds.SoundSource; +-import net.minecraft.tags.TagKey; + import net.minecraft.util.SortedArraySet; + import net.minecraft.util.Unit; + import net.minecraft.world.entity.EntityType; +@@ -115,7 +112,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; +@@ -135,6 +131,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.ScheduledMainThreadTaskQueues; + + public class CraftWorld extends CraftRegionAccessor implements World { + public static final int CUSTOM_DIMENSION_OFFSET = 10; +@@ -2353,11 +2350,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(() -> { ++ ScheduledMainThreadTaskQueues.add(() -> { // Gale - base thread pool + net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)c; + if (chunk != null) addTicket(x, z); // Paper + ret.complete(chunk == null ? null : chunk.getBukkitChunk()); +- }); ++ }, ScheduledMainThreadTaskQueues.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 9368ec01e498f913bc5b7b3e77fe87659090d9b5..54b2a93a2b4708a2e06fa399064105f8f95402ca 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -189,6 +189,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.ScheduledMainThreadTaskQueues; + + public abstract class CraftEntity implements org.bukkit.entity.Entity { + private static PermissibleBase perm; +@@ -1260,7 +1261,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(() -> { ++ ScheduledMainThreadTaskQueues.add(() -> { // Gale - base thread pool + try { + ret.complete(CraftEntity.this.teleport(locationClone, cause) ? Boolean.TRUE : Boolean.FALSE); + } catch (Throwable throwable) { +@@ -1270,7 +1271,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); + } +- }); ++ }, ScheduledMainThreadTaskQueues.TELEPORT_ASYNC_TASK_MAX_DELAY); // Gale - base thread pool + }); + + return ret; +diff --git a/src/main/java/org/galemc/gale/concurrent/LockAndCondition.java b/src/main/java/org/galemc/gale/concurrent/LockAndCondition.java +index 73fd8ca0bd1168862a03d9bdcae93d62895e8c1f..6985e86495fc7046cbe1623d90f72adc95d909e2 100644 +--- a/src/main/java/org/galemc/gale/concurrent/LockAndCondition.java ++++ b/src/main/java/org/galemc/gale/concurrent/LockAndCondition.java +@@ -9,7 +9,7 @@ import java.util.concurrent.locks.Lock; + * A utility class that stores a {@link Condition} with its {@link Lock}, that can be passed around and used instead + * of using an {@link Object} monitor, which does not support speculative locking. + * +- * @author Martijn Muijsers ++ * @author Martijn Muijsers under AGPL-3.0 + */ + public class LockAndCondition { + +diff --git a/src/main/java/org/galemc/gale/concurrent/Mutex.java b/src/main/java/org/galemc/gale/concurrent/Mutex.java +index 76e4d7b2f242218df7e853b416a69d62707357e8..beb233dee5b00eee1ff3b3bc9818a2a70a4421d3 100644 +--- a/src/main/java/org/galemc/gale/concurrent/Mutex.java ++++ b/src/main/java/org/galemc/gale/concurrent/Mutex.java +@@ -17,7 +17,7 @@ import java.util.concurrent.locks.Lock; + * respectively {@link #acquireUninterruptibly}, {@link #acquire}, {@link #tryAcquire} and + * {@link #release}. The {@link Lock#newCondition} method does not have a default implementation. + * +- * @author Martijn Muijsers ++ * @author Martijn Muijsers under AGPL-3.0 + */ + @AnyThreadSafe + public interface Mutex extends Lock { +diff --git a/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java +index f8e3151e6ba1ef0850f50c836962b33f088de375..03bf3a93f683010ea76bbb26ae03c18e4e98d889 100644 +--- a/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java ++++ b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java +@@ -15,7 +15,7 @@ import java.util.concurrent.locks.Lock; + * and throws {@link UnsupportedOperationException} for all {@link Lock} methods that do not have a default + * implementation in {@link Mutex}. + * +- * @author Martijn Muijsers ++ * @author Martijn Muijsers under AGPL-3.0 + */ + @AnyThreadSafe @YieldFree + public class SemaphoreMutex extends Semaphore implements Mutex { +diff --git a/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java b/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java +index c7a4186bb93992733f393ac58fb4d65bbc8db861..c00186a73dc5c705a8a76e6ee532cd63cdf11b4d 100644 +--- a/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java ++++ b/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java +@@ -266,7 +266,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 (!ScheduledMainThreadTaskQueues.writeLock.tryLock()); ++ try { ++ // Update the values in MinecraftServerBlockableEventLoop for quick access ++ ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY = this.defaultValue >= 0 ? this.defaultValue : 2; ++ ScheduledMainThreadTaskQueues.COMPLETE_CHUNK_FUTURE_TASK_MAX_DELAY = this.completeChunkFuture >= 0 ? this.completeChunkFuture : ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; ++ ScheduledMainThreadTaskQueues.POST_CHUNK_LOAD_JOIN_TASK_MAX_DELAY = this.postChunkLoadJoin >= 0 ? this.postChunkLoadJoin : ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; ++ ScheduledMainThreadTaskQueues.ANTI_XRAY_MODIFY_BLOCKS_TASK_MAX_DELAY = this.antiXrayModifyBlocks >= 0 ? this.antiXrayModifyBlocks : ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; ++ ScheduledMainThreadTaskQueues.TELEPORT_ASYNC_TASK_MAX_DELAY = this.teleportAsync >= 0 ? this.teleportAsync : ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; ++ ScheduledMainThreadTaskQueues.SEND_COMMAND_COMPLETION_SUGGESTIONS_TASK_MAX_DELAY = this.sendCommandCompletionSuggestions >= 0 ? this.sendCommandCompletionSuggestions : ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; ++ ScheduledMainThreadTaskQueues.KICK_FOR_COMMAND_PACKET_SPAM_TASK_MAX_DELAY = this.kickForCommandPacketSpam >= 0 ? this.kickForCommandPacketSpam : ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; ++ ScheduledMainThreadTaskQueues.KICK_FOR_RECIPE_PACKET_SPAM_TASK_MAX_DELAY = this.kickForRecipePacketSpam >= 0 ? this.kickForRecipePacketSpam : ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; ++ ScheduledMainThreadTaskQueues.KICK_FOR_BOOK_TOO_LARGE_PACKET_TASK_MAX_DELAY = this.kickForBookTooLargePacket >= 0 ? this.kickForBookTooLargePacket : ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; ++ ScheduledMainThreadTaskQueues.KICK_FOR_EDITING_BOOK_TOO_QUICKLY_TASK_MAX_DELAY = this.kickForEditingBookTooQuickly >= 0 ? this.kickForEditingBookTooQuickly : ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; ++ ScheduledMainThreadTaskQueues.KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY = this.kickForIllegalCharactersInChatPacket >= 0 ? this.kickForIllegalCharactersInChatPacket : ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; ++ ScheduledMainThreadTaskQueues.KICK_FOR_OUT_OF_ORDER_CHAT_PACKET_TASK_MAX_DELAY = this.kickForOutOfOrderChatPacket >= 0 ? this.kickForOutOfOrderChatPacket : ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; ++ ScheduledMainThreadTaskQueues.HANDLE_DISCONNECT_TASK_MAX_DELAY = this.handleDisconnect >= 0 ? this.handleDisconnect : ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY; ++ // Change the length of the pendingRunnables array of queues ++ int maxDelay = 0; ++ for (int delay : new int[]{ ++ ScheduledMainThreadTaskQueues.DEFAULT_TASK_MAX_DELAY, ++ ScheduledMainThreadTaskQueues.COMPLETE_CHUNK_FUTURE_TASK_MAX_DELAY, ++ ScheduledMainThreadTaskQueues.POST_CHUNK_LOAD_JOIN_TASK_MAX_DELAY, ++ ScheduledMainThreadTaskQueues.ANTI_XRAY_MODIFY_BLOCKS_TASK_MAX_DELAY, ++ ScheduledMainThreadTaskQueues.TELEPORT_ASYNC_TASK_MAX_DELAY, ++ ScheduledMainThreadTaskQueues.SEND_COMMAND_COMPLETION_SUGGESTIONS_TASK_MAX_DELAY, ++ ScheduledMainThreadTaskQueues.KICK_FOR_COMMAND_PACKET_SPAM_TASK_MAX_DELAY, ++ ScheduledMainThreadTaskQueues.KICK_FOR_RECIPE_PACKET_SPAM_TASK_MAX_DELAY, ++ ScheduledMainThreadTaskQueues.KICK_FOR_BOOK_TOO_LARGE_PACKET_TASK_MAX_DELAY, ++ ScheduledMainThreadTaskQueues.KICK_FOR_EDITING_BOOK_TOO_QUICKLY_TASK_MAX_DELAY, ++ ScheduledMainThreadTaskQueues.KICK_FOR_ILLEGAL_CHARACTERS_IN_CHAT_PACKET_TASK_MAX_DELAY, ++ ScheduledMainThreadTaskQueues.KICK_FOR_OUT_OF_ORDER_CHAT_PACKET_TASK_MAX_DELAY, ++ ScheduledMainThreadTaskQueues.HANDLE_DISCONNECT_TASK_MAX_DELAY ++ }) { ++ if (delay > maxDelay) { ++ maxDelay = delay; ++ } ++ } ++ int newPendingRunnablesLength = maxDelay + 1; ++ int oldPendingRunnablesLength = ScheduledMainThreadTaskQueues.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 < ScheduledMainThreadTaskQueues.queues.length; i++) { ++ ScheduledMainThreadTaskQueues.queues[maxDelay].addAll(ScheduledMainThreadTaskQueues.queues[i]); ++ } ++ // Update the first queue with elements index ++ if (ScheduledMainThreadTaskQueues.firstQueueWithPotentialTasksIndex >= newPendingRunnablesLength) { ++ ScheduledMainThreadTaskQueues.firstQueueWithPotentialTasksIndex = maxDelay; ++ } ++ } ++ ScheduledMainThreadTaskQueues.queues = Arrays.copyOf(ScheduledMainThreadTaskQueues.queues, newPendingRunnablesLength); ++ if (newPendingRunnablesLength > oldPendingRunnablesLength) { ++ // Create new queues ++ for (int i = oldPendingRunnablesLength; i < newPendingRunnablesLength; i++) { ++ ScheduledMainThreadTaskQueues.queues[i] = new MultiThreadedQueue<>(); ++ } ++ } ++ } ++ } finally { ++ ScheduledMainThreadTaskQueues.writeLock.unlock(); ++ } ++ } ++ ++ } ++ // Gale end - base thread pool ++ + } + + public LogToConsole logToConsole; +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..c8208f47c53df8ee438409b0a954c9dafca6b8fa +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/AbstractBlockableEventLoop.java +@@ -0,0 +1,20 @@ ++// 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(); ++ ++ 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..fe168eb59694415b84ee83f943a7e24b1f3d09da +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/MinecraftServerBlockableEventLoop.java +@@ -0,0 +1,189 @@ ++// 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.OriginalServerThreadOnly; ++import org.galemc.gale.executor.queue.AnyTickScheduledMainThreadTaskQueue; ++import org.galemc.gale.executor.queue.BaseTaskQueues; ++import org.galemc.gale.executor.queue.ScheduledMainThreadTaskQueues; ++import org.galemc.gale.executor.thread.MainThreadClaim; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++import org.slf4j.Logger; ++import org.spigotmc.WatchdogThread; ++ ++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 || !MainThreadClaim.isCurrentThreadMainThreadAndNotClaimable(Thread.currentThread())) && !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 (!MainThreadClaim.isCurrentThreadMainThreadAndNotClaimable(Thread.currentThread())) { ++ this.submitAsync(runnable).join(); ++ } else { ++ runnable.run(); ++ } ++ } ++ ++ /** ++ * @deprecated Use {@link ScheduledMainThreadTaskQueues#add(Runnable, int)} instead: ++ * do not rely on {@link ScheduledMainThreadTaskQueues#DEFAULT_TASK_MAX_DELAY}. ++ */ ++ @Deprecated ++ @Override ++ public void tell(@NotNull Runnable message) { ++ ScheduledMainThreadTaskQueues.add(() -> { ++ if (Thread.currentThread() != WatchdogThread.instance) { ++ MinecraftServer.SERVER.getProfiler().incrementCounter("runTask"); ++ } ++ ++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 { ++ --reentrantCount; ++ } ++ 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 MainThreadClaim.isCurrentThreadMainThreadAndNotClaimable(Thread.currentThread()); ++ } ++ ++ @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. ++ */ ++ @OriginalServerThreadOnly ++ 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 = ScheduledMainThreadTaskQueues.poll(MinecraftServer.SERVER.originalServerThread, 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. ++ */ ++ @OriginalServerThreadOnly ++ 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.anyTickScheduledMainThread.poll(MinecraftServer.SERVER.originalServerThread); ++ if (task == null) { ++ break; ++ } ++ task.run(); ++ } ++ } ++ ++ /** ++ * @deprecated Use {@link #managedBlock(BooleanSupplier, Long)} instead. ++ */ ++ @OriginalServerThreadOnly ++ @Deprecated ++ public void managedBlock(@Nullable BooleanSupplier stopCondition) { ++ managedBlock(stopCondition, null); ++ } ++ ++ @OriginalServerThreadOnly ++ public void managedBlock(@Nullable BooleanSupplier stopCondition, @Nullable Long stopTime) { ++ ++blockingCount; ++ try { ++ MainThreadClaim.secondaryThreadsCanStartToClaim(); ++ try { ++ MinecraftServer.SERVER.originalServerThread.runTasksUntil(stopCondition, null, stopTime); ++ } finally { ++ MainThreadClaim.claimAsOriginalServerThread(); ++ } ++ } finally { ++ --blockingCount; ++ } ++ } ++ ++ @Override ++ public @NotNull String name() { ++ return NAME; ++ } ++ ++} +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 e87ee2612348fc559b21256cc7cadfc684f01f8e..e7ed376efb811e05b5cfecfa31c9c0041e70cf16 100644 +--- a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java ++++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java +@@ -2,6 +2,9 @@ + + package org.galemc.gale.executor.annotation; + ++import org.galemc.gale.executor.lock.YieldingLock; ++import org.galemc.gale.executor.thread.BaseThread; ++ + import java.lang.annotation.Documented; + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; +@@ -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 BaseThread}. + * + * @author Martijn Muijsers under AGPL-3.0 + */ +diff --git a/src/main/java/org/galemc/gale/executor/lock/MultipleWaitingThreadsYieldingLock.java b/src/main/java/org/galemc/gale/executor/lock/MultipleWaitingThreadsYieldingLock.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cc005d17cd4a3b75cb4dcc809df70ded109ad66c +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/lock/MultipleWaitingThreadsYieldingLock.java +@@ -0,0 +1,39 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.lock; ++ ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.wait.WaitingBaseThreadSet; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++ ++import java.util.concurrent.locks.Lock; ++ ++/** ++ * A {@link YieldingLock} for which multiple base threads may be waiting at the same time. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++public class MultipleWaitingThreadsYieldingLock extends YieldingLock { ++ ++ private final WaitingBaseThreadSet waitingThreads = new WaitingBaseThreadSet(); ++ ++ public MultipleWaitingThreadsYieldingLock(Lock innerLock) { ++ super(innerLock); ++ } ++ ++ @Override ++ public void addWaitingThread(BaseThread thread) { ++ waitingThreads.add(thread); ++ } ++ ++ @Override ++ public void removeWaitingThread(BaseThread thread) { ++ waitingThreads.remove(thread); ++ } ++ ++ @Override ++ public SignalReason getSignalReason() { ++ return this.waitingThreads; ++ } ++ ++} +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..9dafb891aeda688ad668cd429c68d063e48429fe +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/lock/YieldingLock.java +@@ -0,0 +1,121 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.lock; ++ ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.PotentiallyYielding; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.concurrent.TimeUnit; ++import java.util.concurrent.locks.Condition; ++import java.util.concurrent.locks.Lock; ++ ++/** ++ * 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 BaseThread}. Acquiring it on a thread that is not a ++ * {@link BaseThread} will perform regular locking on the underlying controlled lock: which typically waits on ++ * failure, blocking the thread in the process. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@AnyThreadSafe ++public abstract class YieldingLock implements Lock { ++ ++ private final Lock innerLock; ++ ++ public YieldingLock(Lock innerLock) { ++ this.innerLock = innerLock; ++ } ++ ++ /** ++ * Attempts to acquire the lock immediately. ++ * ++ * @return Whether the lock was acquired. ++ */ ++ @YieldFree ++ @Override ++ public boolean tryLock() { ++ return innerLock.tryLock(); ++ } ++ ++ /** ++ * Acquires the lock. ++ *
    ++ * If the current threads is a {@link BaseThread}, 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() { ++ // Try to acquire the lock straight away ++ if (!this.innerLock.tryLock()) { ++ // If unsuccessful, we find out our current thread ++ BaseThread baseThread = BaseThread.getCurrent(); ++ // If we are not on a base thread, we wait for the lock instead of yielding ++ if (baseThread == null) { ++ this.innerLock.lock(); ++ return; ++ } ++ // Otherwise, we yield to other tasks until the lock can be acquired ++ baseThread.yieldUntil(null, this); ++ } ++ } ++ ++ /** ++ * Releases the lock (must be called after having completed the computation block that required the lock). ++ */ ++ @Override ++ public void unlock() { ++ this.innerLock.unlock(); ++ // Signal the first waiting thread, if any. ++ // 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). ++ this.getSignalReason().signalAnother(); ++ } ++ ++ @Override ++ public boolean tryLock(long l, @NotNull TimeUnit timeUnit) throws InterruptedException { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @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(); ++ } ++ ++ /** ++ * Adds a thread to the set of threads waiting for this lock to be released. ++ * ++ * @param thread The thread to register as waiting for this lock. ++ */ ++ @YieldFree ++ public abstract void addWaitingThread(BaseThread thread); ++ ++ /** ++ * Removes a thread from the set of threads waiting for this lock to be released. ++ * ++ * @param thread The thread to unregister as waiting for this lock. ++ */ ++ @YieldFree ++ public abstract void removeWaitingThread(BaseThread thread); ++ ++ @YieldFree ++ public abstract SignalReason getSignalReason(); ++ ++} +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..3a79096e5a16798539d45d252672c3650b7a2afd +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/queue/AbstractTaskQueue.java +@@ -0,0 +1,94 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.queue; ++ ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.BaseThreadOnly; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++import org.galemc.gale.executor.thread.wait.TaskWaitingBaseThreads; ++import org.jetbrains.annotations.Nullable; ++ ++/** ++ * An interface 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 interface AbstractTaskQueue { ++ ++ /** ++ * @return Whether the tasks in this queue must be executed on the main thread. ++ */ ++ boolean isMainThreadOnly(); ++ ++ /** ++ * @return Whether this queue has potentially yielding tasks that could start right now. ++ * ++ * @see #hasTasksThatCanStartNow ++ */ ++ boolean hasYieldingTasksThatCanStartNow(); ++ ++ /** ++ * @return Whether this queue has yield-free tasks that could start right now. ++ * ++ * @see #hasTasksThatCanStartNow ++ */ ++ boolean hasFreeTasksThatCanStartNow(); ++ ++ /** ++ * @return Whether this queue has tasks that could start right now. An example of ++ * tasks that could not start right now are tasks in {@link ScheduledMainThreadTaskQueues} that are scheduled for ++ * a later tick, while we are already out of spare time this tick. ++ *
    ++ * 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. ++ */ ++ @BaseThreadOnly ++ default boolean hasTasksThatCanStartNow(BaseThread currentThread) { ++ return (!currentThread.isRestricted && this.hasYieldingTasksThatCanStartNow()) || this.hasFreeTasksThatCanStartNow(); ++ } ++ ++ /** ++ * @return Whether this queue has any tasks at all. ++ */ ++ boolean hasTasks(); ++ ++ /** ++ * Attempts to poll a task. ++ *
    ++ * If the tasks in this queue are main-thread-only or this method may return a main-thread-only task, ++ * the calling thread must have claimed the main thread and ++ * {@link org.galemc.gale.executor.thread.MainThreadClaim#canMainThreadBeClaimed} must be false to prevent the ++ * polling of a main-thread-only task that can then not be immediately started. ++ * ++ * @param currentThread The current thread. ++ * @return The polled task, or null if this queue was empty. ++ */ ++ @BaseThreadOnly ++ @Nullable Runnable poll(BaseThread currentThread); ++ ++ /** ++ * Schedules a new task to this queue. ++ * ++ * @param task The task to schedule. ++ * @param yielding Whether the task is potentially yielding. ++ */ ++ void add(Runnable task, boolean yielding); ++ ++ /** ++ * @return The {@link SignalReason} that will be given to {@link TaskWaitingBaseThreads#signal} to signal new ++ * potentially yielding tasks being added to this queue, or null if irrelevant (e.g. because the signal never ++ * needs to be repeated, or because this queue will never hold potentially yielding tasks). ++ */ ++ @Nullable SignalReason getYieldingSignalReason(); ++ ++ /** ++ * @return The {@link SignalReason} that will be given to {@link TaskWaitingBaseThreads#signal} to signal new ++ * yield-free tasks being added to this queue, or null if irrelevant (e.g. because the signal never ++ * needs to be repeated, or because this queue will never hold yield-free tasks). ++ */ ++ @Nullable SignalReason getFreeSignalReason(); ++ ++} +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..b186e5b7dbd70a2cb3bd2e9559bca791d7ef2fb6 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledChunkCacheTaskQueue.java +@@ -0,0 +1,103 @@ ++// 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.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.MainThreadOnly; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++import org.galemc.gale.executor.thread.wait.WaitingBaseThreadSet; ++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 main thread tasks, and as ++ * such we provide access to polling these tasks for any {@link BaseThread} to execute them as the main thread. ++ *
    ++ * All tasks provided by this queue must be yield-free. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@AnyThreadSafe @YieldFree ++public final class AllLevelsScheduledChunkCacheTaskQueue implements AbstractTaskQueue { ++ ++ public static final SignalReason signalReason = SignalReason.createForTaskQueue(true, true, false); ++ ++ AllLevelsScheduledChunkCacheTaskQueue() {} ++ ++ @Override ++ public boolean isMainThreadOnly() { ++ return true; ++ } ++ ++ @Override ++ public boolean hasYieldingTasksThatCanStartNow() { ++ return false; ++ } ++ ++ @Override ++ public boolean hasFreeTasksThatCanStartNow() { ++ // Skip during server bootstrap or if there is no more time in the current spare time ++ if (MinecraftServer.isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking || MinecraftServer.SERVER == null) { ++ return false; ++ } ++ return this.hasTasks(); ++ } ++ ++ @Override ++ public boolean hasTasks() { ++ for (ServerLevel worldserver : MinecraftServer.SERVER.getAllLevels()) { ++ if (worldserver.getChunkSource().mainThreadProcessor.hasPendingTasks()) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ @MainThreadOnly ++ @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.SERVER == null) { ++ return null; ++ } ++ ServerLevel[] levels = MinecraftServer.SERVER.getAllLevelsArray(); ++ int startIndex = currentThread.allLevelsChunkCacheTaskQueueLevelIterationIndex = Math.min(currentThread.allLevelsChunkCacheTaskQueueLevelIterationIndex, levels.length - 1); ++ // Paper - force execution of all worlds, do not just bias the first ++ do { ++ ServerLevel level = levels[currentThread.allLevelsChunkCacheTaskQueueLevelIterationIndex++]; ++ if (currentThread.allLevelsChunkCacheTaskQueueLevelIterationIndex == levels.length) { ++ currentThread.allLevelsChunkCacheTaskQueueLevelIterationIndex = 0; ++ } ++ if (level.serverLevelArrayIndex != -1) { ++ var executor = level.getChunkSource().mainThreadProcessor; ++ if (executor.hasPendingTasks()) { ++ return executor::pollTask; ++ } ++ } ++ } while (currentThread.allLevelsChunkCacheTaskQueueLevelIterationIndex != startIndex); ++ return null; ++ } ++ ++ @Override ++ public void add(Runnable task, boolean yielding) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public @Nullable SignalReason getYieldingSignalReason() { ++ return null; ++ } ++ ++ @Override ++ public SignalReason getFreeSignalReason() { ++ return signalReason; ++ } ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledMainThreadChunkTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledMainThreadChunkTaskQueue.java +new file mode 100644 +index 0000000000000000000000000000000000000000..02b61e420478d5b782930111dad96de5bd9f9801 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledMainThreadChunkTaskQueue.java +@@ -0,0 +1,104 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.queue; ++ ++import io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.level.ServerChunkCache; ++import net.minecraft.server.level.ServerLevel; ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.MainThreadOnly; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++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 ++ * for any {@link BaseThread} to execute them as the main thread. ++ *
    ++ * All tasks provided by this queue must be yield-free. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@AnyThreadSafe @YieldFree ++public final class AllLevelsScheduledMainThreadChunkTaskQueue implements AbstractTaskQueue { ++ ++ public static final SignalReason signalReason = SignalReason.createForTaskQueue(true, true, false); ++ ++ AllLevelsScheduledMainThreadChunkTaskQueue() {} ++ ++ @Override ++ public boolean isMainThreadOnly() { ++ return true; ++ } ++ ++ @Override ++ public boolean hasYieldingTasksThatCanStartNow() { ++ return false; ++ } ++ ++ @Override ++ public boolean hasFreeTasksThatCanStartNow() { ++ // Skip during server bootstrap or if there is no more time in the current spare time ++ if (MinecraftServer.isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking || MinecraftServer.SERVER == null) { ++ return false; ++ } ++ return this.hasTasks(); ++ } ++ ++ @Override ++ public boolean hasTasks() { ++ for (ServerLevel worldserver : MinecraftServer.SERVER.getAllLevels()) { ++ if (worldserver.chunkTaskScheduler.mainThreadExecutor.hasScheduledUncompletedTasksVolatile()) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ @MainThreadOnly ++ @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.SERVER == null) { ++ return null; ++ } ++ ServerLevel[] levels = MinecraftServer.SERVER.getAllLevelsArray(); ++ int startIndex = currentThread.allLevelsScheduledMainThreadChunkTaskQueueLevelIterationIndex = Math.min(currentThread.allLevelsScheduledMainThreadChunkTaskQueueLevelIterationIndex, levels.length - 1); ++ // Paper - force execution of all worlds, do not just bias the first ++ do { ++ ServerLevel level = levels[currentThread.allLevelsScheduledMainThreadChunkTaskQueueLevelIterationIndex++]; ++ if (currentThread.allLevelsScheduledMainThreadChunkTaskQueueLevelIterationIndex == levels.length) { ++ currentThread.allLevelsScheduledMainThreadChunkTaskQueueLevelIterationIndex = 0; ++ } ++ if (level.serverLevelArrayIndex != -1) { ++ var executor = level.chunkTaskScheduler.mainThreadExecutor; ++ if (executor.hasScheduledUncompletedTasksVolatile()) { ++ return executor::executeTask; ++ } ++ } ++ } while (currentThread.allLevelsScheduledMainThreadChunkTaskQueueLevelIterationIndex != startIndex); ++ return null; ++ } ++ ++ @Override ++ public void add(Runnable task, boolean yielding) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public @Nullable SignalReason getYieldingSignalReason() { ++ return null; ++ } ++ ++ @Override ++ public SignalReason getFreeSignalReason() { ++ return signalReason; ++ } ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/queue/AnyTickScheduledMainThreadTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AnyTickScheduledMainThreadTaskQueue.java +new file mode 100644 +index 0000000000000000000000000000000000000000..014c94a590c1a0d534f2009caae3a2e3b4ddcdd6 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/queue/AnyTickScheduledMainThreadTaskQueue.java +@@ -0,0 +1,68 @@ ++// 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.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.MainThreadOnly; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++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 must be finished by some time in the future, but not necessarily within the current tick or its spare time. ++ *
    ++ * This queue does not support {@link #add}. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@AnyThreadSafe @YieldFree ++public final class AnyTickScheduledMainThreadTaskQueue implements AbstractTaskQueue { ++ ++ AnyTickScheduledMainThreadTaskQueue() {} ++ ++ @Override ++ public boolean isMainThreadOnly() { ++ return true; ++ } ++ ++ @Override ++ public boolean hasYieldingTasksThatCanStartNow() { ++ return ScheduledMainThreadTaskQueues.hasTasksThatCanStartNow(true); ++ } ++ ++ @Override ++ public boolean hasTasks() { ++ return ScheduledMainThreadTaskQueues.hasTasks(true); ++ } ++ ++ @Override ++ public boolean hasFreeTasksThatCanStartNow() { ++ return false; ++ } ++ ++ @MainThreadOnly ++ @Override ++ public @Nullable Runnable poll(BaseThread currentThread) { ++ return ScheduledMainThreadTaskQueues.poll(currentThread, true); ++ } ++ ++ @Override ++ public void add(Runnable task, boolean yielding) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public SignalReason getYieldingSignalReason() { ++ return ScheduledMainThreadTaskQueues.signalReason; ++ } ++ ++ @Override ++ public @Nullable SignalReason getFreeSignalReason() { ++ return null; ++ } ++ ++} +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..c2756a05c87cf3fe11cec82f4a2453e44fe89595 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java +@@ -0,0 +1,91 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.queue; ++ ++import org.galemc.gale.executor.thread.BaseThread; ++ ++/** ++ * This class statically provides a list of task queues containing tasks for {@link BaseThread}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 the main thread, that are procedures that must run ++ * on the main 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 TickTaskQueue}, and by extension in ++ * {@link #anyTickScheduledMainThread} and {@link #scheduledAsync}. ++ *
    ++ * This queue may contain potentially yielding and yield-free tasks. ++ *
    ++ * This queue's {@link AbstractTaskQueue#add} must not be called from the main thread, because the main thread must ++ * not defer to itself (because tasks in this queue are assumed to have to run independent of other main 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 YieldingAndFreeSimpleTaskQueue deferredToMainThread = new YieldingAndFreeSimpleTaskQueue(true, true, 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 #thisTickScheduledMainThread} and {@link #scheduledAsync}. ++ *
    ++ * This queue may contain potentially yielding and yield-free tasks. ++ *
    ++ * 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 YieldingAndFreeSimpleTaskQueue mainThreadTick = new YieldingAndFreeSimpleTaskQueue(true, true); ++ ++ /** ++ * Currently unused. ++ * ++ * @see ThisTickScheduledMainThreadTaskQueue ++ */ ++ public static final ThisTickScheduledMainThreadTaskQueue thisTickScheduledMainThread = new ThisTickScheduledMainThreadTaskQueue(); ++ ++ /** ++ * @see AnyTickScheduledMainThreadTaskQueue ++ */ ++ public static final AnyTickScheduledMainThreadTaskQueue anyTickScheduledMainThread = new AnyTickScheduledMainThreadTaskQueue(); ++ ++ /** ++ * 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}), and have a higher priority ++ * in being started than pending tasks in {@link #scheduledAsync}. ++ *
    ++ * This queue may contain potentially yielding and yield-free tasks. ++ *
    ++ * 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 YieldingAndFreeSimpleTaskQueue baseThreadTick = new YieldingAndFreeSimpleTaskQueue(false, true); ++ ++ /** ++ * @see AllLevelsScheduledChunkCacheTaskQueue ++ */ ++ public static final AllLevelsScheduledChunkCacheTaskQueue allLevelsScheduledChunkCache = new AllLevelsScheduledChunkCacheTaskQueue(); ++ ++ /** ++ * @see AllLevelsScheduledMainThreadChunkTaskQueue ++ */ ++ public static final AllLevelsScheduledMainThreadChunkTaskQueue allLevelsScheduledMainThreadChunk = new AllLevelsScheduledMainThreadChunkTaskQueue(); ++ ++ /** ++ * 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 potentially yielding and yield-free tasks. ++ */ ++ public static final YieldingAndFreeSimpleTaskQueue scheduledAsync = new YieldingAndFreeSimpleTaskQueue(false,false); ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/queue/FreeSimpleTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/FreeSimpleTaskQueue.java +new file mode 100644 +index 0000000000000000000000000000000000000000..01017d6f392ccb5172b4fa4522576ffb50ef06a3 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/queue/FreeSimpleTaskQueue.java +@@ -0,0 +1,53 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.queue; ++ ++import org.galemc.gale.concurrent.UnterminableExecutorService; ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.BaseThreadOnly; ++import org.galemc.gale.executor.annotation.YieldFree; ++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 yield-free. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@BaseThreadOnly @YieldFree ++public class FreeSimpleTaskQueue extends SimpleTaskQueue { ++ ++ FreeSimpleTaskQueue(boolean mainThreadOnly, boolean baseThreadOnly) { ++ super(mainThreadOnly, baseThreadOnly, false, true); ++ } ++ ++ FreeSimpleTaskQueue(boolean mainThreadOnly, boolean baseThreadOnly, boolean lifoQueues) { ++ super(mainThreadOnly, baseThreadOnly, false, true, lifoQueues); ++ } ++ ++ /** ++ * Schedules a new task to this queue. ++ * ++ * @param task The task to schedule. ++ */ ++ @AnyThreadSafe @YieldFree ++ public void add(Runnable task) { ++ this.add(task, false); ++ } ++ ++ /** ++ * 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) { ++ add(runnable, false); ++ } ++ ++ }; ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/queue/ScheduledMainThreadTaskQueues.java b/src/main/java/org/galemc/gale/executor/queue/ScheduledMainThreadTaskQueues.java +new file mode 100644 +index 0000000000000000000000000000000000000000..896f84decd11c6afdcd3f956983215d4e22ba0b7 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/queue/ScheduledMainThreadTaskQueues.java +@@ -0,0 +1,294 @@ ++// 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.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.Guarded; ++import org.galemc.gale.executor.annotation.MainThreadOnly; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++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 potentially yielding: no special distinction for yield-free ++ * tasks is made. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@AnyThreadSafe @YieldFree ++public final class ScheduledMainThreadTaskQueues { ++ ++ ScheduledMainThreadTaskQueues() {} ++ ++ 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; ++ ++ /** ++ * 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. ++ */ ++ @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(); ++ ++ static final SignalReason signalReason = SignalReason.createForTaskQueue(true, true, true); ++ ++ /** ++ * @return Whether there are any scheduled main thread tasks that could start right now. 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. ++ */ ++ public static boolean hasTasksThatCanStartNow(boolean tryNonCurrentTickQueuesAtAll) { ++ return hasTasks(tryNonCurrentTickQueuesAtAll, true); ++ } ++ ++ /** ++ * @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) { ++ return hasTasks(tryNonCurrentTickQueuesAtAll, false); ++ } ++ ++ /** ++ * Common implementation for {@link #hasTasksThatCanStartNow} and {@link #hasTasks(boolean)}. ++ */ ++ private static boolean hasTasks(boolean tryNonCurrentTickQueuesAtAll, boolean checkCanStartNow) { ++ //noinspection StatementWithEmptyBody ++ while (!readLock.tryLock()); ++ try { ++ // Try the queue most likely to contain tasks first ++ if (firstQueueWithPotentialTasksIndex == 0 || (tryNonCurrentTickQueuesAtAll && (!checkCanStartNow || !MinecraftServer.isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking))) { ++ if (!queues[firstQueueWithPotentialTasksIndex].isEmpty()) { ++ return true; ++ } ++ } ++ int checkUpTo = tryNonCurrentTickQueuesAtAll && (!checkCanStartNow || !MinecraftServer.isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking) ? 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. ++ */ ++ @MainThreadOnly ++ public static @Nullable Runnable poll(BaseThread currentThread, boolean tryNonCurrentTickQueuesAtAll) { ++ // Since we assume the tasks in this queue to be potentially yielding, fail if the thread is restricted ++ if (currentThread.isRestricted) { ++ 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; ++ int oldFirstQueueWithElementsIndex = firstQueueWithPotentialTasksIndex; ++ 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() { ++ //noinspection StatementWithEmptyBody ++ while (!readLock.tryLock()); ++ 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 ++ //noinspection StatementWithEmptyBody ++ while (!readLock.tryLock()); ++ try { ++ versionStamp++; ++ queues[maxDelay].add(task); ++ if (maxDelay < firstQueueWithPotentialTasksIndex) { ++ firstQueueWithPotentialTasksIndex = maxDelay; ++ } ++// LockSupport.unpark(MinecraftServer.SERVER.originalServerThread); ++ versionStamp++; ++ } finally { ++ readLock.unlock(); ++ } ++ MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks = true; ++ signalReason.signalAnother(); ++ } ++ ++ /** ++ * This moves the queues in {@link #queues}. ++ */ ++ public static void shiftTasksForNextTick() { ++ //noinspection StatementWithEmptyBody ++ while (!writeLock.tryLock()); ++ try { ++ 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 ++ queues[0].addAll(firstQueue); ++ firstQueue.clear(); ++ 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..bcc14c165011af4dae98769f5dc48a16c676f205 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/queue/SimpleTaskQueue.java +@@ -0,0 +1,185 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.queue; ++ ++import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue; ++import net.minecraft.server.MinecraftServer; ++import org.galemc.gale.collection.FIFOConcurrentLinkedQueue; ++import org.galemc.gale.concurrent.UnterminableExecutorService; ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.MainThreadClaim; ++import org.galemc.gale.executor.thread.SecondaryThread; ++import org.galemc.gale.executor.thread.SecondaryThreadPool; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++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 { ++ ++ /** ++ * Whether the tasks in this queue can only be performed on the main thread. ++ */ ++ public final boolean mainThreadOnly; ++ ++ /** ++ * Whether the tasks in this queue can only be performed on a {@link BaseThread}, ++ * as opposed to being able to be performed on any thread. ++ */ ++ public final boolean baseThreadOnly; ++ ++ /** ++ * Whether tasks in this queue can be potentially yielding. ++ */ ++ public final boolean canHaveYieldingTasks; ++ ++ /** ++ * Whether tasks in this queue can be yield-free. ++ */ ++ public final boolean canHaveFreeTasks; ++ ++ /** ++ * The queue of potentially yielding tasks, or null if {@link #canHaveYieldingTasks} is false. ++ */ ++ private @Nullable Queue yieldingQueue; ++ ++ /** ++ * The queue of yield-free tasks, or null if {@link #canHaveFreeTasks} is false. ++ */ ++ private @Nullable Queue freeQueue; ++ ++ /** ++ * The {@link SignalReason} used when signalling threads due to newly added potentially yielding tasks, ++ * or null if {@link #canHaveYieldingTasks} is false. ++ */ ++ private final @Nullable SignalReason yieldingSignalReason; ++ ++ /** ++ * The {@link SignalReason} used when signalling threads due to newly added yield-free tasks, ++ * or null if {@link #canHaveFreeTasks} is false. ++ */ ++ private final @Nullable SignalReason freeSignalReason; ++ ++ SimpleTaskQueue(boolean mainThreadOnly, boolean baseThreadOnly, boolean canHaveYieldingTasks, boolean canHaveFreeTasks) { ++ this(mainThreadOnly, baseThreadOnly, canHaveYieldingTasks, canHaveFreeTasks, false); ++ } ++ ++ /** ++ * @param mainThreadOnly Value for {@link #mainThreadOnly}. ++ * @param baseThreadOnly Value for {@link #baseThreadOnly}. ++ * @param canHaveYieldingTasks Value for {@link #canHaveYieldingTasks}. ++ * @param canHaveFreeTasks Value for {@link #canHaveFreeTasks}. ++ * @param lifoQueues If true, the {@link #yieldingQueue} and {@link #freeQueue} will be LIFO; ++ * otherwise, they will be FIFO. ++ */ ++ SimpleTaskQueue(boolean mainThreadOnly, boolean baseThreadOnly, boolean canHaveYieldingTasks, boolean canHaveFreeTasks, boolean lifoQueues) { ++ if (mainThreadOnly && !baseThreadOnly) { ++ throw new IllegalArgumentException(); ++ } ++ this.mainThreadOnly = mainThreadOnly; ++ this.baseThreadOnly = baseThreadOnly; ++ this.canHaveYieldingTasks = canHaveYieldingTasks; ++ this.canHaveFreeTasks = canHaveFreeTasks; ++ this.yieldingQueue = this.canHaveYieldingTasks ? (lifoQueues ? new FIFOConcurrentLinkedQueue<>() : new MultiThreadedQueue<>()) : null; ++ this.freeQueue = this.canHaveFreeTasks ? (lifoQueues ? new FIFOConcurrentLinkedQueue<>() : new MultiThreadedQueue<>()) : null; ++ this.yieldingSignalReason = SignalReason.createForTaskQueue(this.mainThreadOnly, this.baseThreadOnly, true); ++ this.freeSignalReason = SignalReason.createForTaskQueue(this.mainThreadOnly, this.baseThreadOnly, false); ++ } ++ ++ @Override ++ public boolean isMainThreadOnly() { ++ return this.mainThreadOnly; ++ } ++ ++ @Override ++ public boolean hasYieldingTasksThatCanStartNow() { ++ return this.canHaveYieldingTasks && !this.yieldingQueue.isEmpty(); ++ } ++ ++ @Override ++ public boolean hasFreeTasksThatCanStartNow() { ++ return this.canHaveFreeTasks && !this.freeQueue.isEmpty(); ++ } ++ ++ @Override ++ public boolean hasTasks() { ++ return (this.canHaveYieldingTasks && !this.yieldingQueue.isEmpty()) || (this.canHaveFreeTasks && !this.freeQueue.isEmpty()); ++ } ++ ++ /** ++ * Attempts to poll a task. ++ * This always returns null on the {@link MinecraftServer#originalServerThread} if ++ * {@link MinecraftServer#canPollAsyncTasksOnOriginalServerThread} is false and {@link #baseThreadOnly} is false, ++ * and always returns null on a {@link SecondaryThread} if ++ * {@link SecondaryThreadPool#canOnlyPollAsyncTasksOnSecondaryThreads} is true and {@link #baseThreadOnly} is true. ++ * ++ * @see AbstractTaskQueue#poll ++ */ ++ @Override ++ public @Nullable Runnable poll(BaseThread currentThread) { ++ boolean isOriginalServerThread = MinecraftServer.SERVER == null || currentThread == MinecraftServer.SERVER.originalServerThread; ++ if (isOriginalServerThread && !this.baseThreadOnly && !MinecraftServer.canPollAsyncTasksOnOriginalServerThread) { ++ return null; ++ } else if (!isOriginalServerThread && this.baseThreadOnly && SecondaryThreadPool.canOnlyPollAsyncTasksOnSecondaryThreads) { ++ return null; ++ } ++ Runnable task; ++ if (!currentThread.isRestricted && this.canHaveYieldingTasks) { ++ task = this.yieldingQueue.poll(); ++ if (task != null) { ++ /* ++ If the thread was woken up for a different reason, signal for the same reason to wake up another ++ potential thread waiting for the reason. ++ */ ++ if (currentThread.lastSignalReason != null && currentThread.lastSignalReason != this.yieldingSignalReason) { ++ currentThread.lastSignalReason.signalAnother(); ++ } ++ return task; ++ } ++ } ++ if (this.canHaveFreeTasks) { ++ task = this.freeQueue.poll(); ++ if (task != null) { ++ /* ++ If the thread was woken up for a different reason, signal for the same reason to wake up another ++ potential thread waiting for the reason. ++ */ ++ if (currentThread.lastSignalReason != null && currentThread.lastSignalReason != this.freeSignalReason) { ++ currentThread.lastSignalReason.signalAnother(); ++ } ++ return task; ++ } ++ } ++ return null; ++ } ++ ++ @Override ++ public void add(Runnable task, boolean yielding) { ++ (yielding ? this.yieldingQueue : this.freeQueue).add(task); ++ if (!this.mainThreadOnly || MainThreadClaim.canMainThreadBeClaimed) { ++ (yielding ? this.yieldingSignalReason : this.freeSignalReason).signalAnother(); ++ } ++ } ++ ++ @Override ++ public @Nullable SignalReason getYieldingSignalReason() { ++ return this.yieldingSignalReason; ++ } ++ ++ @Override ++ public @Nullable SignalReason getFreeSignalReason() { ++ return this.freeSignalReason; ++ } ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/queue/ThisTickScheduledMainThreadTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/ThisTickScheduledMainThreadTaskQueue.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b94bbeb65d232d8015fd5d76c004145db49bc07a +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/queue/ThisTickScheduledMainThreadTaskQueue.java +@@ -0,0 +1,82 @@ ++// 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.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.Guarded; ++import org.galemc.gale.executor.annotation.MainThreadOnly; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++import org.galemc.gale.executor.thread.wait.TaskWaitingBaseThreads; ++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.LockSupport; ++import java.util.concurrent.locks.ReadWriteLock; ++import java.util.concurrent.locks.ReentrantReadWriteLock; ++ ++/** ++ * 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. ++ *
    ++ * This queue does not support {@link #add}. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@AnyThreadSafe @YieldFree ++public final class ThisTickScheduledMainThreadTaskQueue implements AbstractTaskQueue { ++ ++ ThisTickScheduledMainThreadTaskQueue() {} ++ ++ @Override ++ public boolean isMainThreadOnly() { ++ return true; ++ } ++ ++ @Override ++ public boolean hasYieldingTasksThatCanStartNow() { ++ return ScheduledMainThreadTaskQueues.hasTasksThatCanStartNow(false); ++ } ++ ++ @Override ++ public boolean hasTasks() { ++ return ScheduledMainThreadTaskQueues.hasTasks(false); ++ } ++ ++ @Override ++ public boolean hasFreeTasksThatCanStartNow() { ++ return false; ++ } ++ ++ @MainThreadOnly ++ @Override ++ public @Nullable Runnable poll(BaseThread currentThread) { ++ return ScheduledMainThreadTaskQueues.poll(currentThread, false); ++ } ++ ++ @Override ++ public void add(Runnable task, boolean yielding) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public SignalReason getYieldingSignalReason() { ++ return ScheduledMainThreadTaskQueues.signalReason; ++ } ++ ++ @Override ++ public @Nullable SignalReason getFreeSignalReason() { ++ return null; ++ } ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/queue/TickTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/TickTaskQueue.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ee16e3b71c7951062596fef316d27bc66a3ad655 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/queue/TickTaskQueue.java +@@ -0,0 +1,281 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.queue; ++ ++import net.minecraft.server.MinecraftServer; ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.MainThreadOnly; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.MainThreadClaim; ++import org.galemc.gale.executor.thread.SecondaryThread; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++import org.galemc.gale.executor.thread.wait.WaitingBaseThreadSet; ++import org.jetbrains.annotations.Nullable; ++ ++import java.util.Queue; ++import java.util.concurrent.ConcurrentLinkedQueue; ++ ++/** ++ * This class stores the tasks scheduled to be executed on any thread that can participate in ticking, ++ * which is the {@link MinecraftServer#originalServerThread} and any {@link SecondaryThread}. ++ *
    ++ * These tasks are explicitly those that represent steps in ticking the server, and as such always have a ++ * higher priority in being started than pending tasks in ++ * {@link ScheduledMainThreadTaskQueues} and {@link ScheduledAsyncTaskQueue}. ++ *
    ++ * This executor stores four queues, for tasks that: ++ *
      ++ *
    • Must be performed on the main thread, and are potentially yielding
    • ++ *
    • Must be performed on the main thread, and are yield-free
    • ++ *
    • Can be performed on any base thread, an are potentially yielding
    • ++ *
    • Can be performed on any base thread, and are yield-free
    • ++ *
    ++ * 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 tasks are necessarily performed in the order they are added, because they may be in ++ * different queues. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@AnyThreadSafe @YieldFree ++public final class TickTaskQueue { ++ ++ TickTaskQueue() {} ++ ++ /** ++ * The queue of potentially yielding main-thread-only tasks. ++ */ ++ private static final Queue mainThreadYieldingQueue = new ConcurrentLinkedQueue<>(); ++ ++ /** ++ * The queue of yield-free main-thread-only tasks. ++ */ ++ private static final Queue mainThreadFreeQueue = new ConcurrentLinkedQueue<>(); ++ ++ /** ++ * The queue of potentially yielding {@link BaseThread} tasks. ++ */ ++ private static final Queue baseThreadYieldingQueue = new ConcurrentLinkedQueue<>(); ++ ++ /** ++ * The queue of yield-free {@link BaseThread} tasks. ++ */ ++ private static final Queue baseThreadFreeQueue = new ConcurrentLinkedQueue<>(); ++ ++ /** ++ * The base threads waiting for yield-free tasks to be added. ++ */ ++ private static final WaitingBaseThreadSet freeWaitingThreads = new WaitingBaseThreadSet() { ++ ++ @Override ++ public boolean pollAndSignal(SignalReason reason) { ++ if (super.pollAndSignal(reason)) { ++ return true; ++ } ++ return waitingThreads.pollAndSignal(reason); ++ } ++ ++ }; ++ ++ /** ++ * The base threads waiting for tasks to be added. ++ */ ++ private static final WaitingBaseThreadSet waitingThreads = new WaitingBaseThreadSet(); ++ ++ private static final SignalReason mainThreadOnlyFreeOrMoreTaskSignalReason = new SignalReason() { ++ ++ @Override ++ public boolean signalAnother() { ++ if (MainThreadClaim.canMainThreadBeClaimed) { ++ if (freeWaitingThreads.pollAndSignal(this)) { ++ return true; ++ } ++ if (waitingThreads.pollAndSignal(this)) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ }; ++ ++ private static final SignalReason generalMainThreadOnlyTaskSignalReason = new SignalReason() { ++ ++ @Override ++ public boolean signalAnother() { ++ if (MainThreadClaim.canMainThreadBeClaimed) { ++ if (waitingThreads.pollAndSignal(this)) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ }; ++ ++ /** ++ * Attempts to poll a yield-free main-thread-only task. ++ */ ++ @MainThreadOnly ++ public static @Nullable Runnable pollMainThreadOnlyFreeTaskAsMainThread() { ++ return mainThreadFreeQueue.poll(); ++ } ++ ++ /** ++ * Attempts to poll a main-thread-only task. ++ * The different types of tasks are attempted to be polled in the following order: ++ *
      ++ *
    1. Potentially yielding tasks
    2. ++ *
    3. Yield-free tasks
    4. ++ *
    ++ */ ++ @MainThreadOnly ++ public static @Nullable Runnable pollMainThreadOnlyTaskAsMainThread() { ++ Runnable task = mainThreadYieldingQueue.poll(); ++ if (task != null) { ++ return task; ++ } ++ return mainThreadFreeQueue.poll(); ++ } ++ ++ /** ++ * Attempts to poll a yield-free that can be performed on any {@link BaseThread}. ++ */ ++ public static @Nullable Runnable pollNonMainThreadOnlyFreeTask() { ++ return baseThreadFreeQueue.poll(); ++ } ++ ++ /** ++ * Attempts to poll a task that can be performed on any {@link BaseThread}. ++ * The different types of tasks are attempted to be polled in the following order: ++ *
      ++ *
    1. Potentially yielding tasks
    2. ++ *
    3. Yield-free tasks
    4. ++ *
    ++ */ ++ public static @Nullable Runnable pollNonMainThreadOnlyTask() { ++ Runnable task = baseThreadYieldingQueue.poll(); ++ if (task != null) { ++ return task; ++ } ++ return baseThreadFreeQueue.poll(); ++ } ++ ++ /** ++ * @return Whether there are pending potentially yielding main-thread-only tasks. ++ */ ++ public static boolean hasMainThreadOnlyYieldingTasks() { ++ return !mainThreadYieldingQueue.isEmpty(); ++ } ++ ++ /** ++ * @return Whether there are pending potentially yielding {@link BaseThread} tasks. ++ */ ++ public static boolean hasBaseThreadYieldingTasks() { ++ return !baseThreadYieldingQueue.isEmpty(); ++ } ++ ++ /** ++ * @return Whether there are pending yield-free tasks that must be performed on the main thread. ++ */ ++ public static boolean hasMainThreadOnlyFreeTasks() { ++ return !mainThreadFreeQueue.isEmpty(); ++ } ++ ++ /** ++ * @return Whether there are pending main-thread-only tasks. ++ */ ++ public static boolean hasMainThreadOnlyTasks() { ++ return !mainThreadYieldingQueue.isEmpty() || !mainThreadFreeQueue.isEmpty(); ++ } ++ ++ /** ++ * @return Whether there are pending yield-free non-main-thread-only tasks. ++ */ ++ public static boolean hasNonMainThreadOnlyFreeTasks() { ++ return !baseThreadFreeQueue.isEmpty(); ++ } ++ ++ /** ++ * @return Whether there are pending non-main-thread-only. ++ */ ++ public static boolean hasNonMainThreadOnlyTasks() { ++ return !baseThreadYieldingQueue.isEmpty() || !baseThreadFreeQueue.isEmpty(); ++ } ++ ++ /** ++ * @return Whether there are pending tasks. ++ */ ++ public static boolean hasTasks() { ++ return !mainThreadYieldingQueue.isEmpty() || !mainThreadFreeQueue.isEmpty() || !baseThreadYieldingQueue.isEmpty() || !baseThreadFreeQueue.isEmpty(); ++ } ++ ++ /** ++ * Schedules a new task to this queue. ++ * ++ * @param task The task to schedule. ++ * @param mainThreadOnly Whether the task must be performed on the main thread. ++ * @param yielding Whether the task is potentially yielding. ++ */ ++ public static void add(Runnable task, boolean mainThreadOnly, boolean yielding) { ++ (mainThreadOnly ? (yielding ? mainThreadYieldingQueue : mainThreadFreeQueue) : (yielding ? baseThreadYieldingQueue : baseThreadFreeQueue)).add(task); ++ if (!mainThreadOnly || MainThreadClaim.canMainThreadBeClaimed) { ++ if (!yielding) { ++ if (freeWaitingThreads.pollAndSignal(mainThreadOnly ? mainThreadOnlyFreeOrMoreTaskSignalReason : freeWaitingThreads)) { ++ return; ++ } ++ } ++ waitingThreads.pollAndSignal(mainThreadOnly ? generalMainThreadOnlyTaskSignalReason : waitingThreads); ++ } ++ } ++ ++ public static void signalWaitingThreadsAfterMainThreadBecameClaimable() { ++ if (!mainThreadFreeQueue.isEmpty()) { ++ if (freeWaitingThreads.pollAndSignal()) { ++ return; ++ } ++ waitingThreads.pollAndSignal(); ++ return; ++ } ++ if (!mainThreadYieldingQueue.isEmpty()) { ++ waitingThreads.pollAndSignal(); ++ } ++ } ++ ++ /** ++ * Adds a thread to the set of threads waiting for certain new tasks to be added to this queue. ++ * ++ * @param thread The thread to register as waiting for new tasks. ++ * @param isRestricted Whether the given thread is restricted. ++ */ ++ public static void addWaitingThread(BaseThread thread, boolean isRestricted) { ++ (isRestricted ? freeWaitingThreads : waitingThreads).add(thread); ++ } ++ ++ /** ++ * Removes a thread from the set of threads waiting for certain new tasks to be added to this queue. ++ * ++ * @param thread The thread to unregister as waiting for new tasks. ++ * @param isRestricted Whether the given thread was restricted when being added. ++ */ ++ public static void removeWaitingThread(BaseThread thread, boolean isRestricted) { ++ (isRestricted ? freeWaitingThreads : waitingThreads).remove(thread); ++ } ++ ++ public static boolean isNonMainThreadOnlyFreeOrMoreTaskSignalReason(SignalReason reason) { ++ return reason == freeWaitingThreads || reason == waitingThreads; ++ } ++ ++ public static boolean isGeneralNonMainThreadOnlyTaskSignalReason(SignalReason reason) { ++ return reason == waitingThreads; ++ } ++ ++ public static boolean isMainThreadOnlyFreeOrMoreTaskSignalReason(SignalReason reason) { ++ return reason == mainThreadOnlyFreeOrMoreTaskSignalReason || reason == generalMainThreadOnlyTaskSignalReason; ++ } ++ ++ public static boolean isGeneralMainThreadOnlyTaskSignalReason(SignalReason reason) { ++ return reason == generalMainThreadOnlyTaskSignalReason; ++ } ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/queue/YieldingAndFreeSimpleTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/YieldingAndFreeSimpleTaskQueue.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7078d50a575f875828ce8284807f2545b0ca3b0d +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/queue/YieldingAndFreeSimpleTaskQueue.java +@@ -0,0 +1,56 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.queue; ++ ++import org.galemc.gale.concurrent.UnterminableExecutorService; ++import org.galemc.gale.executor.annotation.BaseThreadOnly; ++import org.galemc.gale.executor.annotation.YieldFree; ++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 potentially yielding and tasks that are ++ * yield-free. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@BaseThreadOnly @YieldFree ++public class YieldingAndFreeSimpleTaskQueue extends SimpleTaskQueue { ++ ++ YieldingAndFreeSimpleTaskQueue(boolean mainThreadOnly, boolean baseThreadOnly) { ++ super(mainThreadOnly, baseThreadOnly, true, true); ++ } ++ ++ YieldingAndFreeSimpleTaskQueue(boolean mainThreadOnly, boolean baseThreadOnly, boolean lifoQueues) { ++ super(mainThreadOnly, baseThreadOnly, true, true, lifoQueues); ++ } ++ ++ /** ++ * An executor for adding potentially yielding tasks to this queue, ++ * where {@link Executor#execute} calls {@link #add}. ++ */ ++ public final ExecutorService yieldingExecutor = new UnterminableExecutorService() { ++ ++ @Override ++ public void execute(@NotNull Runnable runnable) { ++ add(runnable, true); ++ } ++ ++ }; ++ ++ /** ++ * An executor for adding yield-free tasks to this queue, ++ * where {@link Executor#execute} calls {@link #add}. ++ */ ++ public final ExecutorService freeExecutor = new UnterminableExecutorService() { ++ ++ @Override ++ public void execute(@NotNull Runnable runnable) { ++ add(runnable, false); ++ } ++ ++ }; ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/queue/YieldingSimpleTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/YieldingSimpleTaskQueue.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e79f426883c77f81c7068c5d019f1708ef0de5f9 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/queue/YieldingSimpleTaskQueue.java +@@ -0,0 +1,53 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.queue; ++ ++import org.galemc.gale.concurrent.UnterminableExecutorService; ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.BaseThreadOnly; ++import org.galemc.gale.executor.annotation.YieldFree; ++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 potentially yielding. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@BaseThreadOnly @YieldFree ++public class YieldingSimpleTaskQueue extends SimpleTaskQueue { ++ ++ YieldingSimpleTaskQueue(boolean mainThreadOnly, boolean baseThreadOnly) { ++ super(mainThreadOnly, baseThreadOnly, true, false); ++ } ++ ++ YieldingSimpleTaskQueue(boolean mainThreadOnly, boolean baseThreadOnly, boolean lifoQueues) { ++ super(mainThreadOnly, baseThreadOnly, true, false, lifoQueues); ++ } ++ ++ /** ++ * Schedules a new task to this queue. ++ * ++ * @param task The task to schedule. ++ */ ++ @AnyThreadSafe @YieldFree ++ public void add(Runnable task) { ++ this.add(task, true); ++ } ++ ++ /** ++ * 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) { ++ add(runnable, true); ++ } ++ ++ }; ++ ++} +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..8c7023025f6990eacf28186dbf42b275447b13f3 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/thread/BaseThread.java +@@ -0,0 +1,564 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.thread; ++ ++import net.minecraft.server.MinecraftServer; ++import org.galemc.gale.executor.annotation.Access; ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.Guarded; ++import org.galemc.gale.executor.annotation.PotentiallyBlocking; ++import org.galemc.gale.executor.annotation.PotentiallyYielding; ++import org.galemc.gale.executor.annotation.ThisBaseThreadOnly; ++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.AllLevelsScheduledChunkCacheTaskQueue; ++import org.galemc.gale.executor.queue.AllLevelsScheduledMainThreadChunkTaskQueue; ++import org.galemc.gale.executor.queue.BaseTaskQueues; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++import org.galemc.gale.executor.thread.wait.TaskWaitingBaseThreads; ++import org.jetbrains.annotations.Nullable; ++import org.spigotmc.WatchdogThread; ++import io.papermc.paper.util.TickThread; ++ ++import java.util.Arrays; ++import java.util.concurrent.TimeUnit; ++import java.util.concurrent.atomic.AtomicInteger; ++import java.util.concurrent.locks.Condition; ++import java.util.concurrent.locks.Lock; ++import java.util.concurrent.locks.ReentrantLock; ++import java.util.function.BooleanSupplier; ++ ++/** ++ * This class allows using instanceof to quickly check if the current thread is a base thread, i.e. ++ * the {@link MinecraftServer#originalServerThread} or a {@link SecondaryThread}, ++ * and contains information present on any base thread. ++ *
    ++ * Since the {@link WatchdogThread#instance} is also a {@link TickThread}, it is also an instance of this class. ++ * When active, it behaves as much as possible like the {@link MinecraftServer#originalServerThread} because only one of them ++ * may be usable at a time. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++public abstract class BaseThread extends Thread { ++ ++ /** ++ * The maximum yield depth, beyond which a thread can no longer start more potentially yielding tasks. ++ */ ++ public static final int MAXIMUM_YIELD_DEPTH = Integer.getInteger("gale.max.yield.depth", 100); ++ ++ /** ++ * Wait for a limited time only before briefly waking up. This is the minimum time to wait for. ++ *
    ++ * This at least serves to wake up the thread to re-check the {@code stopCondition} in {@link #runTasksUntil}. ++ */ ++ private static final long MIN_WAIT_CONDITION_TIMEOUT_MICROS = 1L; ++ ++ /** ++ * Equivalent to {@link #MIN_WAIT_CONDITION_TIMEOUT_MICROS}, but this is the default time to wait for, ++ * unless a non-null {@code stopTime} is provided to {@link #await}. ++ */ ++ private static final long DEFAULT_WAIT_CONDITION_TIMEOUT_MICROS = 100L; ++ ++ public static final AbstractTaskQueue[] taskQueues = { ++ BaseTaskQueues.deferredToMainThread, ++ BaseTaskQueues.mainThreadTick, ++ BaseTaskQueues.anyTickScheduledMainThread, ++ BaseTaskQueues.baseThreadTick, ++ BaseTaskQueues.allLevelsScheduledChunkCache, ++ BaseTaskQueues.allLevelsScheduledMainThreadChunk, ++ BaseTaskQueues.scheduledAsync ++ }; ++ ++ public static final AbstractTaskQueue[] mainThreadOnlyTaskQueues; ++ public static final AbstractTaskQueue[] nonMainThreadOnlyTaskQueues; ++ static { ++ mainThreadOnlyTaskQueues = Arrays.stream(taskQueues).filter(AbstractTaskQueue::isMainThreadOnly).toArray(AbstractTaskQueue[]::new); ++ nonMainThreadOnlyTaskQueues = Arrays.stream(taskQueues).filter(queue -> !queue.isMainThreadOnly()).toArray(AbstractTaskQueue[]::new); ++ } ++ ++ public final int baseThreadIndex; ++ ++ /** ++ * The current yield depth of this thread. ++ */ ++ @ThisBaseThreadOnly ++ private int yieldDepth = 0; ++ ++ /** ++ * Whether this thread is currently restricted. ++ */ ++ @ThisBaseThreadOnly ++ public boolean isRestricted = false; ++ ++ /** ++ * The queue that the last polled task was successfully polled from. ++ */ ++ @ThisBaseThreadOnly ++ private AbstractTaskQueue lastPolledFromTaskQueue = null; ++ ++ /** ++ * Whether this thread is currently executing a main-thread-only task. ++ */ ++ @ThisBaseThreadOnly ++ public final AtomicInteger mainThreadOnlyTasksBeingExecutedCount = new AtomicInteger(); ++ ++ /** ++ * 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}. ++ */ ++ @AnyThreadSafe(Access.READ) @ThisBaseThreadOnly(Access.WRITE) ++ protected volatile boolean isPollingTask = 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 ++ private volatile boolean skipNextWait = false; ++ ++ /** ++ * Whether this thread is currently waiting for something to do. ++ */ ++ @AnyThreadSafe(Access.READ) @ThisBaseThreadOnly(Access.WRITE) ++ @Guarded("#waitLock") ++ private volatile boolean isWaiting = 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) { ++ super(target, name); ++ this.baseThreadIndex = baseThreadIndex; ++ } ++ ++ /** ++ * Yields to tasks, which means incrementing the yield depth, and polling and executing tasks while possible ++ * and the stop condition is not met, then decrementing the yield depth again. ++ * 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 wait 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. ++ */ ++ @ThisBaseThreadOnly @PotentiallyYielding("this method is meant to yield") ++ public void yieldUntil(@Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock) { ++ this.yieldDepth++; ++ if (!this.isRestricted) { ++ if (this.yieldDepth >= MAXIMUM_YIELD_DEPTH || this.mainThreadOnlyTasksBeingExecutedCount.get() > 0) { ++ this.isRestricted = true; ++ SecondaryThreadPool.threadBecameRestricted(); ++ } ++ } ++ this.runTasksUntil(stopCondition, yieldingLock, null); ++ this.yieldDepth--; ++ if (this.isRestricted) { ++ if (this.yieldDepth < MAXIMUM_YIELD_DEPTH && (this.yieldDepth == 0 || this.mainThreadOnlyTasksBeingExecutedCount.get() == 0)) { ++ this.isRestricted = false; ++ SecondaryThreadPool.threadIsNoLongerRestricted(); ++ } ++ } ++ } ++ ++ /** ++ * This method will keep attempting to find a task to do, and execute it, 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. ++ * The loop is broken as soon as the stop condition becomes true, or the given lock is successfully acquired, ++ * or {@link System#nanoTime} reaches the given {@code stopTime}. ++ *
    ++ * At least one of {@code stopCondition}, {@code yieldingLock} and {@code stopTime} must be non-null. ++ * Only one the above parameters may be non-null, except for the pair of {@code stopCondition} and {@code stopTime}: ++ * in which case running tasks continues until the stop condition becomes true, but {@link #await} is called ++ * with the given {@code stopTime}. ++ * ++ * @see #yieldUntil ++ */ ++ @ThisBaseThreadOnly @PotentiallyYielding("may yield further if an executed task is potentially yielding") ++ public void runTasksUntil(@Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock, @Nullable Long stopTime) { ++ /* ++ 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. ++ */ ++ /* ++ Make sure we do not allow the main thread to be claimed in between the moment we finish ++ executing a main-thread-only task, and we can poll the next one: we must as well stay the main thread if ++ we immediately start another main-thread-only task. ++ */ ++ boolean stillHaveToSetMainThreadCanBeClaimed = false; ++ this.skipNextWait = false; ++ // Reset the last signal reason before the next poll ++ this.lastSignalReason = null; ++ this.isPollingTask = true; ++ while (stopCondition != null ? !stopCondition.getAsBoolean() : yieldingLock != null ? !yieldingLock.tryLock() : System.nanoTime() < stopTime) { ++ MinecraftServer.isInSpareTimeAndHaveNoMoreTimeAndNotAlreadyBlocking = MinecraftServer.isInSpareTime && MinecraftServer.blockingCount == 0 && !MinecraftServer.SERVER.haveTime(); ++ if (this.canAcceptTasks()) { ++ // Get a task that we can execute ++ Runnable task = this.pollTask(); ++ if (task != null) { ++ boolean isTaskMainThreadOnly = this.lastPolledFromTaskQueue.isMainThreadOnly(); ++ if (isTaskMainThreadOnly) { ++ stillHaveToSetMainThreadCanBeClaimed = true; ++ /* ++ We successfully polled a main-thread-only task, if there are more left in the queue, ++ make sure we skip the {@link MinecraftServer#mayHaveDelayedTasks()} check next time. ++ */ ++ if (!MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks && this.lastPolledFromTaskQueue.hasTasks()) { ++ MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks = true; ++ } ++ } else { ++ /* ++ If we still have to allow other threads to become the main thread, ++ do so before starting a task that is not main-thread-only. ++ */ ++ if (stillHaveToSetMainThreadCanBeClaimed) { ++ stillHaveToSetMainThreadCanBeClaimed = false; ++ MainThreadClaim.setMainThreadCanBeClaimed(); ++ } ++ } ++ this.isPollingTask = false; ++ if (isTaskMainThreadOnly) { ++ this.mainThreadOnlyTasksBeingExecutedCount.incrementAndGet(); ++ } ++ task.run(); ++ if (isTaskMainThreadOnly) { ++ MinecraftServer.SERVER.executeMidTickTasks(); // Paper - execute chunk tasks mid tick ++ } ++ if (isTaskMainThreadOnly) { ++ this.mainThreadOnlyTasksBeingExecutedCount.decrementAndGet(); ++ } ++ this.isPollingTask = true; ++ // Reset the last signal reason before the next poll ++ this.lastSignalReason = null; ++ continue; ++ } ++ } ++ // If we still have to allow other threads to become the main thread, do so before waiting ++ if (stillHaveToSetMainThreadCanBeClaimed) { ++ stillHaveToSetMainThreadCanBeClaimed = false; ++ if (this.mainThreadOnlyTasksBeingExecutedCount.get() == 0) { ++ MainThreadClaim.setMainThreadCanBeClaimed(); ++ } ++ } ++ /* ++ If no task that can be executed was found, wait for one to become available, ++ or for the given yielding lock to be released. This is the only time we should ever block inside ++ a potentially yielding procedure. ++ */ ++ this.await(yieldingLock, stopTime); ++ } ++ if (this.lastSignalReason != null && yieldingLock != null && this.lastSignalReason != yieldingLock.getSignalReason()) { ++ // The thread was signalled for another reason than the lock - but we acquired the lock instead ++ this.lastSignalReason.signalAnother(); ++ } ++ this.isPollingTask = false; ++ /* ++ If we still have to allow other threads to become the main thread, do so before exiting running tasks. ++ */ ++ if (stillHaveToSetMainThreadCanBeClaimed) { ++ if (this.mainThreadOnlyTasksBeingExecutedCount.get() == 0) { ++ MainThreadClaim.setMainThreadCanBeClaimed(); ++ } ++ } ++ } ++ ++ /** ++ * @return Whether this thread can currently accept more tasks. This is true if either the task is not restricted ++ * (i.e. it has not exceeded the maximum yield depth), or if the task is restricted but there are no pending ++ * potentially yielding tasks that a replacement thread could start if this thread were to idle. ++ */ ++ @ThisBaseThreadOnly @YieldFree ++ protected boolean canAcceptTasks() { ++ /* ++ This thread cannot accept new tasks if it is a surplus thread in the thread pool, ++ and not enough threads are restricted to warrant use of this surplus thread. ++ */ ++ if (this.baseThreadIndex >= SecondaryThreadPool.intendedSecondaryParallelism + 1 + SecondaryThreadPool.restrictedBaseThreadCount.get()) { ++ return false; ++ } ++ /* ++ Otherwise, this thread can always accept new tasks if it is not restricted, i.e. if it has not exceeded the ++ maximum yield depth and if it is not currently yielding from a main-thread-only task. ++ */ ++ if (!this.isRestricted) { ++ return true; ++ } ++ /* ++ Lastly, check if there are any yielding tasks that could be performed by other non-restricted: ++ if there are, since we are restricted, we should let other threads start executing those tasks instead. ++ */ ++ for (var queue : nonMainThreadOnlyTaskQueues) { ++ if (queue.hasYieldingTasksThatCanStartNow()) { ++ return false; ++ } ++ } ++ if (MainThreadClaim.canMainThreadBeClaimed) { ++ for (var queue : mainThreadOnlyTaskQueues) { ++ if (queue.hasYieldingTasksThatCanStartNow()) { ++ return false; ++ } ++ } ++ } ++ return true; ++ } ++ ++// public boolean doesAnyMainThreadOnlyQueueHaveAppropriateTasks() { ++// for (var queue : mainThreadOnlyTaskQueues) { ++// if (queue.hasTasksThatCanStartNow(this)) { ++// return true; ++// } ++// } ++// return false; ++// } ++ ++ /** ++ * Polls a task from any queue this thread can currently poll from, and returns it. ++ * Polling main-thread-only tasks is attempted before polling tasks that can be performed on any thread, ++ * and secondarily, polling potentially yielding tasks is attempted before yield-free tasks. ++ * ++ * @return The task that was polled, or null if no task was found. ++ */ ++ @ThisBaseThreadOnly @YieldFree ++ protected @Nullable Runnable pollTask() { ++ boolean attemptedToClaimMeanThread = false; ++ boolean hasJustClaimedMainThread = false; ++ for (var queue : taskQueues) { ++ // Claim the main thread if needed for this queue ++ boolean isQueueMainThreadOnly = queue.isMainThreadOnly(); ++ if (isQueueMainThreadOnly) { ++ if (!attemptedToClaimMeanThread) { ++ // Don't attempt to claim the main thread if this queue is empty anyway ++ if (!queue.hasTasksThatCanStartNow(this)) { ++ continue; ++ } ++ if (MainThreadClaim.attemptToClaim(this, true)) { ++ hasJustClaimedMainThread = true; ++ } ++ attemptedToClaimMeanThread = true; ++ } ++ // Skip this queue if we have failed to claim the main thread ++ if (!hasJustClaimedMainThread) { ++ continue; ++ } ++ } ++ Runnable task = queue.poll(this); ++ if (task != null) { ++ // We successfully polled a task ++ if (hasJustClaimedMainThread && !isQueueMainThreadOnly) { ++ /* ++ We claimed the main thread but polled from a queue that is not main-thread-only, so we must ++ mark the main thread as claimable again. ++ */ ++ MainThreadClaim.setMainThreadCanBeClaimed(); ++ } ++ this.lastPolledFromTaskQueue = queue; ++ return task; ++ } ++ } ++ if (hasJustClaimedMainThread) { ++ /* ++ We lost a race condition between the hasTasksThatCanStartNow checks and the poll call to a main-thread-only ++ task queue: let's just mark that the main thread can be claimed by another thread, and return that we ++ failed to poll a task. ++ */ ++ MainThreadClaim.setMainThreadCanBeClaimed(); ++ } ++ // We failed to poll any task ++ return null; ++ } ++ ++ /** ++ * Performs a dry run of {@link #pollTask}, returning whether a task would have been polled. ++ * ++ * @see #pollTask ++ */ ++ @ThisBaseThreadOnly @YieldFree ++ protected boolean couldPollTask() { ++ boolean checkedIfCouldClaimMeanThread = false; ++ boolean couldClaimMainThread = false; ++ for (var queue : taskQueues) { ++ // Check if this thread could claim the main thread if needed for this queue ++ boolean alreadyVerifiedHasTasksThatCanStartNow = false; ++ if (queue.isMainThreadOnly()) { ++ if (!checkedIfCouldClaimMeanThread) { ++ // Don't attempt to check to claim the main thread if this queue is empty anyway ++ if (!queue.hasTasksThatCanStartNow(this)) { ++ continue; ++ } ++ alreadyVerifiedHasTasksThatCanStartNow = true; ++ if (MainThreadClaim.couldClaim(this)) { ++ couldClaimMainThread = true; ++ } ++ checkedIfCouldClaimMeanThread = true; ++ } ++ // Skip this queue if we could not claim the main thread ++ if (!couldClaimMainThread) { ++ continue; ++ } ++ } ++ if (alreadyVerifiedHasTasksThatCanStartNow || queue.hasTasksThatCanStartNow(this)) { ++ return true; ++ } ++ } ++ // We failed to peek any task ++ return 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. ++ */ ++ @AnyThreadSafe @YieldFree ++ public boolean signal(@Nullable SignalReason reason) { ++ //noinspection StatementWithEmptyBody ++ while (!this.waitLock.tryLock()); ++ try { ++ if (this.isWaiting) { ++ this.lastSignalReason = reason; ++ this.waitCondition.signal(); ++ return true; ++ } else if (this.isPollingTask) { ++ this.lastSignalReason = reason; ++ this.skipNextWait = true; ++ return true; ++ } ++ return false; ++ } finally { ++ this.waitLock.unlock(); ++ } ++ } ++ ++ /** ++ * Starts waiting on something to do. ++ * ++ * @param yieldingLock A {@link YieldingLock} to register with, or null if this thread is not waiting for ++ * a yielding lock. ++ * @param stopTime The maximum moment to wait until, or null if no specific time is known, in which case this ++ * thread will await being signalled without a timeout. ++ */ ++ @ThisBaseThreadOnly @PotentiallyBlocking ++ private void await(@Nullable YieldingLock yieldingLock, @Nullable Long stopTime) { ++ // Register this thread with parties that may signal it ++ boolean registeredAsWaiting = false; ++ if (!this.skipNextWait) { ++ // No point in registering if we're not going to wait anyway ++ this.registerAsWaiting(yieldingLock); ++ registeredAsWaiting = true; ++ } ++ // If we cannot acquire the lock, this thread is being signalled, so there is no reason to start waiting. ++ if (this.waitLock.tryLock()) { ++ try { ++ if (!this.skipNextWait) { ++ if (!registeredAsWaiting) { ++ this.registerAsWaiting(yieldingLock); ++ registeredAsWaiting = true; ++ } ++ // Do a quick last check to not sleep if something changed since last checking ++ if (!this.couldPollTask()) { ++ // Wait ++ this.isWaiting = true; ++ try { ++ if (stopTime == null) { ++ this.waitCondition.await(); ++ } else { ++ this.waitCondition.await(Math.max(MIN_WAIT_CONDITION_TIMEOUT_MICROS, Math.max((stopTime - System.nanoTime()) / 1000L, DEFAULT_WAIT_CONDITION_TIMEOUT_MICROS)), TimeUnit.MICROSECONDS); ++ } ++ } catch (InterruptedException e) { ++ throw new IllegalStateException(e); ++ } ++ this.isWaiting = false; ++ } ++ } ++ this.skipNextWait = false; ++ } finally { ++ this.waitLock.unlock(); ++ } ++ } ++ // Unregister this thread from the parties it was registered with before ++ if (registeredAsWaiting) { ++ this.unregisterAsWaiting(yieldingLock); ++ } ++ } ++ ++ /** ++ * Registers this thread in various places that will help to identify this thread as a candidate to signal ++ * in case of changes that allow this thread to continue work. ++ * ++ * @param yieldingLock A {@link YieldingLock} to register with, or null if this thread is not waiting for ++ * a yielding lock. ++ */ ++ @ThisBaseThreadOnly @YieldFree ++ private void registerAsWaiting(@Nullable YieldingLock yieldingLock) { ++ // Register with the queues that we may poll tasks from ++ TaskWaitingBaseThreads.add(this); ++ // Register with the lock ++ if (yieldingLock != null) { ++ yieldingLock.addWaitingThread(this); ++ } ++ } ++ ++ /** ++ * Unregisters this thread from the places it was registered with in {@link #registerAsWaiting}. ++ */ ++ @ThisBaseThreadOnly @YieldFree ++ private void unregisterAsWaiting(@Nullable YieldingLock yieldingLock) { ++ // Unregister from the task queues ++ TaskWaitingBaseThreads.remove(this); ++ // Unregister from the lock ++ if (yieldingLock != null) { ++ yieldingLock.removeWaitingThread(this); ++ } ++ } ++ ++ /** ++ * A thread-local iteration index for iterating over the levels in ++ * {@link AllLevelsScheduledChunkCacheTaskQueue#poll}. ++ */ ++ public int allLevelsChunkCacheTaskQueueLevelIterationIndex; ++ ++ /** ++ * A thread-local iteration index for iterating over the levels in ++ * {@link AllLevelsScheduledMainThreadChunkTaskQueue#poll}. ++ */ ++ public int allLevelsScheduledMainThreadChunkTaskQueueLevelIterationIndex; ++ ++ /** ++ * @return The current thread if it is a {@link BaseThread}, or null otherwise. ++ */ ++ @AnyThreadSafe @YieldFree ++ public static @Nullable BaseThread getCurrent() { ++ if (Thread.currentThread() instanceof BaseThread baseThread) { ++ return baseThread; ++ } ++ return null; ++ } ++ ++ /** ++ * @return Whether the current thread is a {@link BaseThread}. ++ */ ++ @AnyThreadSafe @YieldFree ++ public static boolean isBaseThread() { ++ return Thread.currentThread() instanceof BaseThread; ++ } ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/thread/MainThreadClaim.java b/src/main/java/org/galemc/gale/executor/thread/MainThreadClaim.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d424c53c3d627d1e5265081ff99dbca6b91f15f4 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/thread/MainThreadClaim.java +@@ -0,0 +1,250 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.thread; ++ ++import net.minecraft.server.MinecraftServer; ++import org.galemc.gale.concurrent.Mutex; ++import org.galemc.gale.executor.MinecraftServerBlockableEventLoop; ++import org.galemc.gale.executor.annotation.Access; ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.BaseThreadOnly; ++import org.galemc.gale.executor.annotation.Guarded; ++import org.galemc.gale.executor.annotation.MainThreadOnly; ++import org.galemc.gale.executor.annotation.OriginalServerThreadOnly; ++import org.galemc.gale.executor.annotation.PotentiallyYielding; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++import org.galemc.gale.executor.thread.wait.TaskWaitingBaseThreads; ++import org.jetbrains.annotations.Nullable; ++ ++import java.util.function.BooleanSupplier; ++ ++/** ++ * This class serves to remove the notion of 'main thread' from any specific thread, and instead make it ++ * a specific designation that any thread can claim. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++public final class MainThreadClaim { ++ ++ private MainThreadClaim() {} ++ ++ /** ++ * Whether the main thread can currently be claimed. This is set to false after a thread becomes the main thread ++ * and is still attempting to poll a main thread task, or when a main-thread-only task is active (including ++ * the code surrounding any task execution loop calls made by the {@link MinecraftServer#originalServerThread}). ++ */ ++ @AnyThreadSafe(Access.READ) @MainThreadOnly(Access.WRITE) ++ @Guarded(value = "#lock", fieldAccess = Access.WRITE, except = "when only speculatively read") ++ public static volatile boolean canMainThreadBeClaimed = false; ++ ++ /** ++ * Whether the {@link MinecraftServer#originalServerThread} is wanting to become the main thread so that it ++ * can exit a part of code during which secondary threads can become the main thread. ++ * To be precise, the main thread can be claimed by secondary threads during ticking the server (because ++ * the {@link MinecraftServer#originalServerThread} then starts doing the same task-execution loop as secondary ++ * threads, with the exception that it will break this loop when ticking finishes - and to break the loop it ++ * must be the main thread) and during a {@link MinecraftServerBlockableEventLoop#managedBlock(BooleanSupplier)} ++ * during spare time that follows ticking (because during that time, other threads should be able to become the main ++ * thread to make sure main-thread-only tasks get executed speedily when they become available while the ++ * {@link MinecraftServer#originalServerThread} is working on non-main-thread-only tasks). ++ *
    ++ * This value is true until the {@link MinecraftServer#originalServerThread} starts allowing secondary threads ++ * to claim the main thread again. ++ */ ++ @AnyThreadSafe(Access.READ) @OriginalServerThreadOnly(Access.WRITE) ++ @Guarded(value = "#lock", fieldAccess = Access.WRITE) ++ private static volatile boolean isOriginalServerThreadWantingToClaim = true; ++ ++ /** ++ * The current main thread. Will be first initialized to {@link MinecraftServer#originalServerThread}. ++ */ ++ @AnyThreadSafe(Access.READ) ++ @Guarded(value = "#lock", fieldAccess = Access.WRITE, except = "in initialize(), where acquiring the lock is unnecessary") ++ private static volatile Thread mainThread; ++ ++ /** ++ * A lock for {@link #canMainThreadBeClaimed} and {@link #mainThread}, which are jointly ++ * checked or updated. ++ */ ++ private static final Mutex lock = Mutex.create(); ++ ++ /** ++ * Sets the current main thread to {@link MinecraftServer#originalServerThread}. ++ */ ++ @OriginalServerThreadOnly ++ public static void initialize() { ++ mainThread = MinecraftServer.SERVER.originalServerThread;; ++ } ++ ++ /** ++ * @deprecated Use {@link #attemptToClaim(Thread, boolean)} instead. ++ */ ++ @BaseThreadOnly @YieldFree ++ @Deprecated ++ public static boolean attemptToClaim(boolean setCanMainThreadBeClaimedToFalse) { ++ return attemptToClaim(Thread.currentThread(), setCanMainThreadBeClaimedToFalse); ++ } ++ ++ /** ++ * Makes the current thread the main thread, if possible. ++ * This is possible if and only if {@link #canMainThreadBeClaimed} is true or the current thread ++ * is already the main thread. ++ * If successful, and {@code setCanMainThreadBeClaimedToFalse} is true, ++ * sets {@link #canMainThreadBeClaimed} to false. ++ * ++ * @return Whether the current thread is now the main thread. ++ */ ++ @BaseThreadOnly @YieldFree ++ public static boolean attemptToClaim(Thread currentThread, boolean setCanMainThreadBeClaimedToFalse) { ++ // Fast pre-check to avoid acquiring the lock ++ if (!canMainThreadBeClaimed || (isOriginalServerThreadWantingToClaim && currentThread != MinecraftServer.SERVER.originalServerThread)) { ++ /* ++ If true, we are already the main thread and no-one can have claimed it by this point. ++ If false cannot become the main thread right now, maybe it just became possible during the last ++ line of code, but it's not worth acquiring the lock for. ++ */ ++ if (mainThread == currentThread) { ++ if (!setCanMainThreadBeClaimedToFalse || !canMainThreadBeClaimed) { ++ // We can only return if we do not still need to set canMainThreadBeClaimed ++ return true; ++ } ++ } else { ++ return false; ++ } ++ } ++ // Acquire the lock and make an attempt ++ //noinspection StatementWithEmptyBody ++ while (!lock.tryAcquire()); ++ try { ++ if (!canMainThreadBeClaimed || (isOriginalServerThreadWantingToClaim && currentThread != MinecraftServer.SERVER.originalServerThread)) { ++ if (mainThread == currentThread) { ++ if (setCanMainThreadBeClaimedToFalse) { ++ canMainThreadBeClaimed = false; ++ } ++ return true; ++ } ++ return false; ++ } ++ mainThread = currentThread; ++ if (setCanMainThreadBeClaimedToFalse) { ++ canMainThreadBeClaimed = false; ++ } ++ return true; ++ } finally { ++ lock.release(); ++ } ++ } ++ ++ /** ++ * @deprecated Use {@link #couldClaim(Thread)} instead. ++ */ ++ @BaseThreadOnly @YieldFree ++ @Deprecated ++ public static boolean couldClaim() { ++ return couldClaim(Thread.currentThread()); ++ } ++ ++ /** ++ * Performs a dry run of {@link #attemptToClaim(Thread, boolean)}, returning the same result but not actually ++ * claiming the main thread. ++ * ++ * @see #attemptToClaim(Thread, boolean) ++ */ ++ @BaseThreadOnly @YieldFree ++ public static boolean couldClaim(Thread currentThread) { ++ // Fast pre-check to avoid acquiring the lock ++ if (!canMainThreadBeClaimed || (isOriginalServerThreadWantingToClaim && currentThread != MinecraftServer.SERVER.originalServerThread)) { ++ /* ++ If true, we are already the main thread and no-one can have claimed it by this point. ++ If false cannot become the main thread right now, maybe it just became possible during the last ++ line of code, but it's not worth acquiring the lock for. ++ */ ++ return mainThread == currentThread; ++ } ++ return true; ++ } ++ ++ /** ++ * Sets {@link #canMainThreadBeClaimed} to true: must only be called from the current main thread. ++ */ ++ @MainThreadOnly @YieldFree ++ public static void setMainThreadCanBeClaimed() { ++ //noinspection StatementWithEmptyBody ++ while (!lock.tryAcquire()); ++ try { ++ canMainThreadBeClaimed = true; ++ } finally { ++ lock.release(); ++ } ++ signalWaitingThreads(); ++ } ++ ++ /** ++ * @deprecated Use {@link #isCurrentThreadMainThreadAndNotClaimable(Thread)} instead. ++ */ ++ @AnyThreadSafe @YieldFree ++ @Deprecated ++ public static boolean isCurrentThreadMainThreadAndNotClaimable() { ++ return isCurrentThreadMainThreadAndNotClaimable(Thread.currentThread()); ++ } ++ ++ @AnyThreadSafe @YieldFree ++ public static boolean isCurrentThreadMainThreadAndNotClaimable(Thread currentThread) { ++ return !canMainThreadBeClaimed && currentThread == mainThread; ++ } ++ ++ /** ++ * Called by the {@link MinecraftServer#originalServerThread} to indicate that it requires being the main thread, ++ * and secondary threads can no longer claim the main thread from now on. ++ *
    ++ * This method waits until the main thread can be claimed for the current thread, yielding to pending tasks ++ * until then. ++ */ ++ @OriginalServerThreadOnly @PotentiallyYielding ++ public static void claimAsOriginalServerThread() { ++ //noinspection StatementWithEmptyBody ++ while (!lock.tryAcquire()); ++ try { ++ isOriginalServerThreadWantingToClaim = true; ++ } finally { ++ lock.release(); ++ } ++ MinecraftServer.SERVER.originalServerThread.yieldUntil(() -> attemptToClaim(MinecraftServer.SERVER.originalServerThread, true), null); ++ } ++ ++ /** ++ * Called by the {@link MinecraftServer#originalServerThread} to indicate that secondary threads can ++ * claim the main thread from now on. ++ */ ++ @OriginalServerThreadOnly @MainThreadOnly @YieldFree ++ public static void secondaryThreadsCanStartToClaim() { ++ //noinspection StatementWithEmptyBody ++ while (!lock.tryAcquire()); ++ try { ++ isOriginalServerThreadWantingToClaim = false; ++ canMainThreadBeClaimed = true; ++ } finally { ++ lock.release(); ++ } ++ signalWaitingThreads(); ++ } ++ ++ private static void signalWaitingThreads() { ++ for (var queue : BaseThread.mainThreadOnlyTaskQueues) { ++ if (queue.hasYieldingTasksThatCanStartNow()) { ++ @Nullable SignalReason yieldingSignalReason = queue.getYieldingSignalReason(); ++ if (yieldingSignalReason != null && yieldingSignalReason.signalAnother()) { ++ return; ++ } ++ } ++ if (queue.hasFreeTasksThatCanStartNow()) { ++ @Nullable SignalReason freeSignalReason = queue.getFreeSignalReason(); ++ if (freeSignalReason != null && freeSignalReason.signalAnother()) { ++ return; ++ } ++ } ++ } ++ } ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/thread/MainThreadDeferral.java b/src/main/java/org/galemc/gale/executor/thread/MainThreadDeferral.java +new file mode 100644 +index 0000000000000000000000000000000000000000..017a5ab8c2468cc59142be7c28a78622e6646dde +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/thread/MainThreadDeferral.java +@@ -0,0 +1,132 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.thread; ++ ++import org.galemc.gale.concurrent.UnterminableExecutorService; ++import org.galemc.gale.executor.queue.BaseTaskQueues; ++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 a {@link BaseThread}, to defer blocks of code to the ++ * main thread, and wait for their 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 ++ * the main thread and wait for that to happen. ++ *
    ++ * This has a number of advantages. Code that checks whether it is being run on the main thread can be run this ++ * way. Since these parts of code are always performed on the main thread 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 threads. Also, we can yield from a {@link BaseThread} until the code has been executed. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++public final class MainThreadDeferral { ++ ++ private MainThreadDeferral() {} ++ ++ /** ++ * @see #defer(Supplier, boolean) ++ */ ++ public static void defer(Runnable task, boolean yielding) { ++ defer(task, null, yielding); ++ } ++ ++ /** ++ * Runs the given {@code task} on the main thread, and yields until it has finished. ++ * If this thread is already the main thread, the task will be executed right away. ++ *
    ++ * Like any potentially yielding method, while technically possible to call from any thread, this method should ++ * generally only be called from a {@link BaseThread}, because on any other thread, the thread will block until ++ * the given task has been completed by the main thread. ++ * ++ * @param task The task to run on the main thread. ++ * @param yielding Whether the given {@code task} is potentially yielding. ++ */ ++ public static T defer(Supplier task, boolean yielding) { ++ return defer(null, task, yielding); ++ } ++ ++ /** ++ * Common implementation for {@link #defer(Runnable, boolean)} and {@link #defer(Supplier, boolean)}. ++ * Exactly one of {@code runnable} or {@code supplier} must be non-null. ++ */ ++ private static T defer(@Nullable Runnable runnable, @Nullable Supplier supplier, boolean yielding) { ++ // Become the main thread if possible ++ BaseThread baseThread = BaseThread.getCurrent(); ++ if (baseThread != null && MainThreadClaim.attemptToClaim(baseThread, true)) { ++ // Run the task right away ++ baseThread.mainThreadOnlyTasksBeingExecutedCount.incrementAndGet(); ++ T output; ++ if (supplier == null) { ++ runnable.run(); ++ output = null; ++ } else { ++ output = supplier.get(); ++ } ++ if (baseThread.mainThreadOnlyTasksBeingExecutedCount.decrementAndGet() == 0) { ++ // Make the main thread claimable again ++ MainThreadClaim.setMainThreadCanBeClaimed(); ++ } ++ return output; ++ } ++ // Otherwise, schedule the task and wait for it to complete ++ CompletableFuture future = new CompletableFuture<>(); ++ if (baseThread != null) { ++ // Yield until the task completes ++ BaseTaskQueues.deferredToMainThread.add(() -> { ++ if (supplier == null) { ++ runnable.run(); ++ future.complete(null); ++ } else { ++ future.complete(supplier.get()); ++ } ++ baseThread.signal(null); ++ }, yielding); ++ baseThread.yieldUntil(future::isDone, null); ++ return future.getNow(null); ++ } else { ++ // Block until the task completes ++ BaseTaskQueues.deferredToMainThread.add(() -> { ++ if (supplier == null) { ++ runnable.run(); ++ future.complete(null); ++ } else { ++ future.complete(supplier.get()); ++ } ++ }, yielding); ++ return future.join(); ++ } ++ } ++ ++ /** ++ * An executor for deferring potentially 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, true); ++ } ++ ++ }; ++ ++ /** ++ * An executor for deferring yield-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, false); ++ } ++ ++ }; ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/thread/SecondaryThread.java b/src/main/java/org/galemc/gale/executor/thread/SecondaryThread.java +new file mode 100644 +index 0000000000000000000000000000000000000000..14ce3eb68983b7d53062e16bfa0da352562364a6 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/thread/SecondaryThread.java +@@ -0,0 +1,42 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.thread; ++ ++import net.minecraft.server.MinecraftServer; ++import org.galemc.gale.executor.annotation.BaseThreadOnly; ++import org.galemc.gale.executor.annotation.ThisBaseThreadOnly; ++ ++/** ++ * A secondary thread to perform tasks in parallel to the {@link MinecraftServer#originalServerThread}. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++public class SecondaryThread extends BaseThread { ++ ++ public final int secondaryThreadIndex; ++ ++ public SecondaryThread(int secondaryThreadIndex) { ++ super(SecondaryThread::getInstanceAndRunForever, "Secondary Thread " + secondaryThreadIndex, secondaryThreadIndex + 1); ++ this.secondaryThreadIndex = secondaryThreadIndex; ++ } ++ ++ /** ++ * This is the main thread loop for a {@link SecondaryThread}. ++ * It will 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. ++ */ ++ @ThisBaseThreadOnly ++ private void runForever() { ++ this.runTasksUntil(() -> false, null, null); ++ } ++ ++ /** ++ * A method that simply acquires the {@link SecondaryThread} that is the current thread, and calls ++ * {@link #runForever()} on it. ++ */ ++ @BaseThreadOnly ++ private static void getInstanceAndRunForever() { ++ ((SecondaryThread) Thread.currentThread()).runForever(); ++ } ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/thread/SecondaryThreadPool.java b/src/main/java/org/galemc/gale/executor/thread/SecondaryThreadPool.java +new file mode 100644 +index 0000000000000000000000000000000000000000..312c57bd7d4ea627e39b797c782be3f893564b22 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/thread/SecondaryThreadPool.java +@@ -0,0 +1,225 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.thread; ++ ++import net.minecraft.server.MinecraftServer; ++import org.galemc.gale.concurrent.Mutex; ++import org.galemc.gale.executor.annotation.Access; ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.BaseThreadOnly; ++import org.galemc.gale.executor.annotation.Guarded; ++import org.galemc.gale.executor.annotation.OriginalServerThreadOnly; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.wait.SignalReason; ++import org.galemc.gale.util.CPUCoresEstimation; ++ ++import java.util.Arrays; ++import java.util.concurrent.atomic.AtomicInteger; ++ ++/** ++ * A pool of threads that, in addition to the main thread, all non-blocking tasks can be performed on. ++ * This pool manages the amount of threads necessary, and activates and de-activates surplus threads to avoid ++ * context switches. Threads created for this pool are instances of {@link SecondaryThread}. ++ *
    ++ * This pool intends to keep {@link #intendedSecondaryParallelism} + 1 threads active at any time, which is initially ++ * the {@link MinecraftServer#originalServerThread} and {@link #intendedSecondaryParallelism} secondary threads. ++ * It allows more secondary threads to become active if an existing active thread is unable to accept more tasks ++ * because it has become restricted. A {@link BaseThread} is restricted when it can not accept new tasks. This happens ++ * when it can only accept yield-free tasks, but there are still pending potentially yielding tasks, which means we ++ * prefer other threads to become active and start those potentially yielding tasks. A thread can only accept yield-free ++ * tasks when it has reached its maximum yield depth or is attempting to yield from a main-thread-only task. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++public final class SecondaryThreadPool { ++ ++ private SecondaryThreadPool() {} ++ ++ /** ++ * Whether {@link SecondaryThread}s can poll only tasks that can run on any thread. ++ *
    ++ * This is true when {@link #intendedSecondaryParallelism} is first determined to have to be 0. In that case, ++ * it is set to 1 instead, so that we at least have one secondary thread to perform asynchronous tasks that ++ * may take so long they prevent a tick from finishing. ++ */ ++ public static final boolean canOnlyPollAsyncTasksOnSecondaryThreads; ++ ++ /** ++ * The minimum number of threads that will be actively in use by this pool. ++ *
    ++ * By default, we always do not use one core, so that there is always a core available for the main thread, or ++ * to run other important threads such as Netty, I/O or garbage collection on. ++ * This also means that if the system has 1 core only, we do not use any secondary threads by default. ++ *
    ++ * This value is never negative. ++ *
    ++ * The value is currently automatically determined according to the following table: ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ *
    system corescores spared
    ≤ 31
    [4, 14]2
    [15, 23]3
    [24, 37]4
    [38, 54]5
    [55, 74]6
    [75, 99]7
    [100, 127]8
    [128, 158]9
    [159, 193]10
    [194, 232]11
    [233, 274]12
    ≥ 27513
    ++ * Then minimum number of secondary threads = system cores - cores spared. ++ */ ++ public static final int intendedSecondaryParallelism; ++ static { ++ int parallelismByEnvironmentVariable = Integer.getInteger("gale.threads.secondary", -1); ++ int intendedSecondaryParallelismBeforeSetAtLeastOne; ++ if (parallelismByEnvironmentVariable >= 0) { ++ intendedSecondaryParallelismBeforeSetAtLeastOne = parallelismByEnvironmentVariable; ++ } else { ++ int systemCores = CPUCoresEstimation.get(); ++ int coresSpared; ++ if (systemCores <= 3) { ++ coresSpared = 1; ++ } else if (systemCores <= 14) { ++ coresSpared = 2; ++ } else if (systemCores <= 23) { ++ coresSpared = 3; ++ } else if (systemCores <= 37) { ++ coresSpared = 4; ++ } else if (systemCores <= 54) { ++ coresSpared = 5; ++ } else if (systemCores <= 74) { ++ coresSpared = 6; ++ } else if (systemCores <= 99) { ++ coresSpared = 7; ++ } else if (systemCores <= 127) { ++ coresSpared = 8; ++ } else if (systemCores <= 158) { ++ coresSpared = 9; ++ } else if (systemCores <= 193) { ++ coresSpared = 10; ++ } else if (systemCores <= 232) { ++ coresSpared = 11; ++ } else if (systemCores <= 274) { ++ coresSpared = 12; ++ } else { ++ coresSpared = 13; ++ } ++ intendedSecondaryParallelismBeforeSetAtLeastOne = systemCores - coresSpared; ++ } ++ if (intendedSecondaryParallelismBeforeSetAtLeastOne >= 1) { ++ intendedSecondaryParallelism = intendedSecondaryParallelismBeforeSetAtLeastOne; ++ canOnlyPollAsyncTasksOnSecondaryThreads = false; ++ } else { ++ intendedSecondaryParallelism = 1; ++ canOnlyPollAsyncTasksOnSecondaryThreads = true; ++ } ++ ++ } ++ ++ /** ++ * The base threads, which is an array of the {@link MinecraftServer#originalServerThread} at index 0, and ++ * {@link #secondaryThreads} afterwards. In other words, it is an array of every {@link BaseThread} among ++ * {@link MinecraftServer#originalServerThread} and {@link #secondaryThreads}, indexed by their ++ * {@link BaseThread#baseThreadIndex}. ++ */ ++ @AnyThreadSafe(Access.READ) @BaseThreadOnly(Access.WRITE) ++ @Guarded(value = "#threadsLock", fieldAccess = Access.WRITE, except = "during static initialization, where acquiring the lock is unnecessary") ++ private static BaseThread[] baseThreads = new BaseThread[intendedSecondaryParallelism + 1]; ++ ++ @OriginalServerThreadOnly @YieldFree ++ public static void setOriginalServerThreadInBaseThread() { ++ baseThreads[0] = MinecraftServer.SERVER.originalServerThread; ++ } ++ ++ @AnyThreadSafe @YieldFree ++ public static BaseThread getBaseThreadByIndex(int baseThreadIndex) { ++ return baseThreads[baseThreadIndex]; ++ } ++ ++ /** ++ * The secondary threads. It is an array of every {@link SecondaryThread}, indexed by their ++ * {@link SecondaryThread#secondaryThreadIndex}. ++ */ ++ @AnyThreadSafe(Access.READ) @BaseThreadOnly(Access.WRITE) ++ @Guarded(value = "#threadsLock", fieldAccess = Access.WRITE, except = "during static initialization, where acquiring the lock is unnecessary") ++ private static SecondaryThread[] secondaryThreads = new SecondaryThread[intendedSecondaryParallelism]; ++ ++ static { ++ for (int i = 0; i < secondaryThreads.length; i++) { ++ baseThreads[i + 1] = secondaryThreads[i] = new SecondaryThread(i); ++ } ++ } ++ ++ @AnyThreadSafe @YieldFree ++ public static SecondaryThread getSecondaryThreadByIndex(int secondaryThreadIndex) { ++ return secondaryThreads[secondaryThreadIndex]; ++ } ++ ++ private static final Mutex threadsLock = Mutex.create(); ++ ++ /** ++ * The number of base threads that are currently restricted. ++ */ ++ @AnyThreadSafe(Access.READ) @BaseThreadOnly(Access.WRITE) ++ public static final AtomicInteger restrictedBaseThreadCount = new AtomicInteger(); ++ ++ private static final SignalReason surplusThreadActivationSignalReason = SignalReason.createNonRetrying(); ++ ++ /** ++ * Starts all secondary threads in this pool. ++ */ ++ @OriginalServerThreadOnly @YieldFree ++ public static void startSecondaryThreads() { ++ //noinspection StatementWithEmptyBody ++ while (!threadsLock.tryAcquire()); ++ try { ++ for (SecondaryThread secondaryThread : secondaryThreads) { ++ secondaryThread.start(); ++ } ++ } finally { ++ threadsLock.release(); ++ } ++ } ++ ++ /** ++ * Called by a {@link BaseThread} when it becomes restricted. ++ */ ++ @BaseThreadOnly @YieldFree ++ public static void threadBecameRestricted() { ++ int newSecondaryThreadCount = restrictedBaseThreadCount.incrementAndGet() + intendedSecondaryParallelism; ++ if (newSecondaryThreadCount > secondaryThreads.length) { ++ //noinspection StatementWithEmptyBody ++ while (!threadsLock.tryAcquire()); ++ try { ++ // Check again to make sure secondaryThreads hasn't already been extended since the last check ++ int oldSecondaryThreadCount = secondaryThreads.length; ++ if (newSecondaryThreadCount > oldSecondaryThreadCount) { ++ // Add surplus threads ++ secondaryThreads = Arrays.copyOf(secondaryThreads, newSecondaryThreadCount); ++ baseThreads = Arrays.copyOf(baseThreads, newSecondaryThreadCount + 1); ++ for (int i = oldSecondaryThreadCount; i < newSecondaryThreadCount; i++) { ++ baseThreads[i + 1] = secondaryThreads[i] = new SecondaryThread(i); ++ secondaryThreads[i].start(); ++ } ++ } ++ } finally { ++ threadsLock.release(); ++ } ++ } ++ // Wake up the appropriate surplus thread if it was sleeping ++ secondaryThreads[newSecondaryThreadCount - 1].signal(surplusThreadActivationSignalReason); ++ } ++ ++ /** ++ * Called by a {@link BaseThread} when it becomes no longer restricted. ++ */ ++ @BaseThreadOnly @YieldFree ++ public static void threadIsNoLongerRestricted() { ++ restrictedBaseThreadCount.decrementAndGet(); ++ } ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/thread/wait/SignalReason.java b/src/main/java/org/galemc/gale/executor/thread/wait/SignalReason.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1c56597ccce2f11db1c8a903e24c07eeb2143564 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/thread/wait/SignalReason.java +@@ -0,0 +1,43 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.thread.wait; ++ ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.BaseThread; ++ ++/** ++ * An interface to indicate the reason of a call to {@link BaseThread#signal}. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++public interface SignalReason { ++ ++ /** ++ * Signals another {@link BaseThread} that is waiting for the same reason. ++ * ++ * @return Whether any thread was signalled. ++ */ ++ boolean signalAnother(); ++ ++ @AnyThreadSafe @YieldFree ++ static SignalReason createForTaskQueue(boolean mainThreadOnly, boolean baseThreadOnly, boolean yielding) { ++ if (mainThreadOnly && !baseThreadOnly) { ++ throw new IllegalArgumentException(); ++ } ++ return new SignalReason() { ++ ++ @Override ++ public boolean signalAnother() { ++ return TaskWaitingBaseThreads.signal(this, mainThreadOnly, baseThreadOnly, yielding); ++ } ++ ++ }; ++ } ++ ++ @AnyThreadSafe @YieldFree ++ static SignalReason createNonRetrying() { ++ return () -> false; ++ } ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/thread/wait/TaskWaitingBaseThreads.java b/src/main/java/org/galemc/gale/executor/thread/wait/TaskWaitingBaseThreads.java +new file mode 100644 +index 0000000000000000000000000000000000000000..2623dea4cb52c16d43749538a5c0254e1ea92667 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/thread/wait/TaskWaitingBaseThreads.java +@@ -0,0 +1,124 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.thread.wait; ++ ++import net.minecraft.server.MinecraftServer; ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.BaseThreadOnly; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.MainThreadClaim; ++import org.galemc.gale.executor.thread.SecondaryThreadPool; ++ ++/** ++ * This class keeps track of all the {@link BaseThread}s that are waiting for tasks to be added to a task queue. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++public final class TaskWaitingBaseThreads { ++ ++ private TaskWaitingBaseThreads() {} ++ ++ /** ++ * The base threads waiting for potentially yielding {@link BaseThread}-only tasks to be added. ++ */ ++ private static final WaitingBaseThreadSet forBaseThreadYieldingTasks = new WaitingBaseThreadSet(); ++ ++ /** ++ * The base threads waiting for yield-free {@link BaseThread}-only tasks to be added. ++ */ ++ private static final WaitingBaseThreadSet forBaseThreadFreeTasks = new WaitingBaseThreadSet() { ++ ++ @Override ++ public boolean pollAndSignal(SignalReason reason) { ++ if (super.pollAndSignal(reason)) { ++ return true; ++ } ++ /* ++ Waiting for BaseThread-only potentially yielding tasks implies waiting for BaseThread-only ++ yield-free tasks, so we signal thread waiting for potentially yielding tasks too. ++ */ ++ return forBaseThreadYieldingTasks.pollAndSignal(reason); ++ } ++ ++ }; ++ ++ /** ++ * The base threads waiting for potentially yielding any-thread tasks to be added. ++ */ ++ private static final WaitingBaseThreadSet forAnyThreadYieldingTasks = new WaitingBaseThreadSet(); ++ ++ /** ++ * The base threads waiting for yield-free any-thread tasks to be added. ++ */ ++ private static final WaitingBaseThreadSet forAnyThreadFreeTasks = new WaitingBaseThreadSet() { ++ ++ @Override ++ public boolean pollAndSignal(SignalReason reason) { ++ if (super.pollAndSignal(reason)) { ++ return true; ++ } ++ /* ++ Waiting for any-thread potentially yielding tasks implies waiting for any-thread ++ yield-free tasks, so we signal thread waiting for potentially yielding tasks too. ++ */ ++ return forAnyThreadYieldingTasks.pollAndSignal(reason); ++ } ++ ++ }; ++ ++ /** ++ * Signal a thread due to a new task being added. ++ * ++ * @param reason The {@link SignalReason} that the signal will originate from. ++ * @param mainThreadOnly Whether the added task is main-thread-only. ++ * @param baseThreadOnly Whether the added task is {@link BaseThread}-only. ++ * @param yielding Whether the added task is potentially yielding. ++ * @return Whether a thread was signalled (this return value is always accurate). ++ */ ++ @AnyThreadSafe @YieldFree ++ public static boolean signal(SignalReason reason, boolean mainThreadOnly, boolean baseThreadOnly, boolean yielding) { ++ if (mainThreadOnly && !MainThreadClaim.canMainThreadBeClaimed) { ++ return false; ++ } ++ return (baseThreadOnly ? (yielding ? forBaseThreadYieldingTasks : forBaseThreadFreeTasks) : (yielding ? forAnyThreadYieldingTasks : forAnyThreadFreeTasks)).pollAndSignal(reason); ++ } ++ ++ /** ++ * Adds a thread to the sets of threads waiting for new tasks to be added to task queues. ++ * ++ * @param currentThread The current thread, as well as the thread to register as waiting for new tasks. ++ */ ++ @BaseThreadOnly @YieldFree ++ public static void add(BaseThread currentThread) { ++ boolean isOriginalServerThread = MinecraftServer.SERVER == null || currentThread == MinecraftServer.SERVER.originalServerThread; ++ // If the thread can accept tasks that must run on a BaseThread, wait for those ++ if (isOriginalServerThread || !SecondaryThreadPool.canOnlyPollAsyncTasksOnSecondaryThreads) { ++ if (!currentThread.isRestricted) { ++ forBaseThreadYieldingTasks.add(currentThread); ++ } ++ forBaseThreadFreeTasks.add(currentThread); ++ } ++ // If the thread can accept tasks that can run on any thread, wait for those ++ if (!isOriginalServerThread || MinecraftServer.canPollAsyncTasksOnOriginalServerThread) { ++ if (!currentThread.isRestricted) { ++ forAnyThreadYieldingTasks.add(currentThread); ++ } ++ forAnyThreadFreeTasks.add(currentThread); ++ } ++ } ++ ++ /** ++ * Removes a thread from the sets of threads waiting for new tasks. ++ * ++ * @param currentThread The current thread, as well as the thread to unregister as waiting for new tasks. ++ */ ++ @BaseThreadOnly @YieldFree ++ public static void remove(BaseThread currentThread) { ++ forBaseThreadYieldingTasks.remove(currentThread); ++ forBaseThreadFreeTasks.remove(currentThread); ++ forAnyThreadYieldingTasks.remove(currentThread); ++ forAnyThreadFreeTasks.remove(currentThread); ++ } ++ ++} +diff --git a/src/main/java/org/galemc/gale/executor/thread/wait/WaitingBaseThreadSet.java b/src/main/java/org/galemc/gale/executor/thread/wait/WaitingBaseThreadSet.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b06c3a0e299e2907547510f88ee00742b9bd8150 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/thread/wait/WaitingBaseThreadSet.java +@@ -0,0 +1,75 @@ ++// Gale - base thread pool ++ ++package org.galemc.gale.executor.thread.wait; ++ ++import org.galemc.gale.executor.annotation.AnyThreadSafe; ++import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.lock.YieldingLock; ++import org.galemc.gale.executor.thread.BaseThread; ++import org.galemc.gale.executor.thread.SecondaryThreadPool; ++ ++import java.util.concurrent.ConcurrentSkipListSet; ++ ++/** ++ * A set of waiting {@link BaseThread}s. This is used to collect the threads that are all waiting for new work, ++ * e.g. for new tasks to be added to a queue or for a {@link YieldingLock} to be released. ++ * ++ * @author Martijn Muijsers under AGPL-3.0 ++ */ ++@AnyThreadSafe @YieldFree ++public class WaitingBaseThreadSet implements SignalReason { ++ ++ /** ++ * A set of the {@link BaseThread#baseThreadIndex} of the threads in this collection. ++ */ ++ private final ConcurrentSkipListSet skipListSet = new ConcurrentSkipListSet<>(); ++ ++ /** ++ * Adds a waiting {@link BaseThread}. ++ */ ++ public void add(BaseThread thread) { ++ this.skipListSet.add(thread.baseThreadIndex); ++ } ++ ++ /** ++ * Removes a waiting {@link BaseThread}. ++ */ ++ public void remove(BaseThread thread) { ++ this.skipListSet.add(thread.baseThreadIndex); ++ } ++ ++ public boolean pollAndSignal() { ++ return this.pollAndSignal(this); ++ } ++ ++ /** ++ * Attempts to signal one waiting {@link BaseThread}. ++ *
    ++ * Calling this method twice from different threads concurrently ++ * will never lead to signalling the same waiting thread: ++ * every thread is only signalled by this set at most one time per continuous period of time that it is present. ++ *
    ++ * Calling this method may fail to signal a newly added thread that is added after this method is called, ++ * but before it returns, and may also fail to signal a just removed thread ++ * that was removed after this method was called, but before it returned. ++ * ++ * @return Whether a thread was signalled (this return value is always accurate). ++ * ++ * @see BaseThread#signal ++ */ ++ @AnyThreadSafe @YieldFree ++ public boolean pollAndSignal(SignalReason reason) { ++ Integer baseThreadIndex = this.skipListSet.pollFirst(); ++ if (baseThreadIndex != null) { ++ SecondaryThreadPool.getBaseThreadByIndex(baseThreadIndex).signal(reason); ++ return true; ++ } ++ return false; ++ } ++ ++ @Override ++ public boolean signalAnother() { ++ return this.pollAndSignal(); ++ } ++ ++} +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 832f1ee4fb11c981bd109510eb908d7c7ef91bd4..970908ba3eafbc79f01c0681e214dfdb95658ec6 100644 +--- a/src/main/java/org/spigotmc/WatchdogThread.java ++++ b/src/main/java/org/spigotmc/WatchdogThread.java +@@ -13,7 +13,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa + { + + 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 +@@ -206,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.getServer().originalServerThread.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/0115-Remove-bootstrap-executor.patch b/patches/server/0115-Remove-bootstrap-executor.patch deleted file mode 100644 index 00f1052..0000000 --- a/patches/server/0115-Remove-bootstrap-executor.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers -Date: Tue, 29 Nov 2022 01:23:49 +0100 -Subject: [PATCH] Remove bootstrap executor - -License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) - -This patch is based on the following patch: -"completely remove bootstrapExecutor" -By: foss-mc <69294560+foss-mc@users.noreply.github.com> -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/Util.java b/src/main/java/net/minecraft/Util.java -index e14245a77b40fca4bacf82295ad390339aba08a9..d84c721ed64cf927ccc9884235b22414298ce3ed 100644 ---- a/src/main/java/net/minecraft/Util.java -+++ b/src/main/java/net/minecraft/Util.java -@@ -80,8 +80,8 @@ 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 BOOTSTRAP_EXECUTOR = makeExecutor("Bootstrap", -2); // Paper - add -2 priority - private static final ExecutorService BACKGROUND_EXECUTOR = AsyncExecutor.instance; // Gale - centralized async execution - remove background executor -+ private static final ExecutorService BOOTSTRAP_EXECUTOR = BACKGROUND_EXECUTOR; // Gale - Patina - remove bootstrap 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() { - diff --git a/patches/server/0116-Remove-world-upgrade-executors.patch b/patches/server/0116-Remove-world-upgrade-executors.patch deleted file mode 100644 index 914fe19..0000000 --- a/patches/server/0116-Remove-world-upgrade-executors.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers -Date: Tue, 29 Nov 2022 14:44:23 +0100 -Subject: [PATCH] Remove world upgrade executors - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) - -diff --git a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java -index 95cac7edae8ac64811fc6a2f6b97dd4a0fceb0b0..29a33ec067b9f47a7a86cce90316e1837d7d8612 100644 ---- a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java -+++ b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java -@@ -18,6 +18,8 @@ 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.concurrent.AsyncExecutor; -+ - import java.io.File; - import java.io.IOException; - import java.text.DecimalFormat; -@@ -46,6 +48,10 @@ public class ThreadedWorldUpgrader { - this.dimensionType = dimensionType; - this.worldName = worldName; - this.worldDir = worldDir; -+ // Gale start - centralized async execution - remove world upgrade executors -+ this.threadPool = AsyncExecutor.instance; -+ /* -+ // Gale end - centralized async execution - remove world upgrade executors - this.threadPool = Executors.newFixedThreadPool(Math.max(1, threads), new ThreadFactory() { - private final AtomicInteger threadCounter = new AtomicInteger(); - -@@ -61,6 +67,7 @@ public class ThreadedWorldUpgrader { - return ret; - } - }); -+ */ // Gale - centralized async execution - remove world upgrade executors - this.dataFixer = dataFixer; - this.generatorKey = generatorKey; - this.removeCaches = removeCaches; diff --git a/patches/server/0117-Remove-tab-complete-executor.patch b/patches/server/0117-Remove-tab-complete-executor.patch deleted file mode 100644 index dd668e5..0000000 --- a/patches/server/0117-Remove-tab-complete-executor.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers -Date: Tue, 29 Nov 2022 14:58:26 +0100 -Subject: [PATCH] Remove tab complete executor - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-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 a531bfe125f991bf372843f365640316c25e334a..98ceec4404c08b08bc8d917730858eec40cfeb9e 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -186,6 +186,7 @@ import net.minecraft.world.phys.shapes.BooleanOp; - import net.minecraft.world.phys.shapes.Shapes; - import net.minecraft.world.phys.shapes.VoxelShape; - import org.apache.commons.lang3.StringUtils; -+import org.galemc.gale.concurrent.AsyncExecutor; - import org.galemc.gale.concurrent.MinecraftServerBlockableEventLoop; - import org.galemc.gale.configuration.GaleGlobalConfiguration; - import org.slf4j.Logger; -@@ -888,8 +889,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 = AsyncExecutor.instance; // Gale - centralized async execution - remove tab complete executor - // Paper end - @Override - public void handleCustomCommandSuggestions(ServerboundCommandSuggestionPacket packet) { diff --git a/patches/server/0118-Remove-text-filter-executor.patch b/patches/server/0118-Remove-text-filter-executor.patch deleted file mode 100644 index b8fdee2..0000000 --- a/patches/server/0118-Remove-text-filter-executor.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MartijnMuijsers -Date: Tue, 29 Nov 2022 15:15:10 +0100 -Subject: [PATCH] Remove text filter executor - -License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) - -diff --git a/src/main/java/net/minecraft/server/network/TextFilterClient.java b/src/main/java/net/minecraft/server/network/TextFilterClient.java -index 2393b6a5f3f12c2b17b172ee8ca42ead218e2a10..38f70925693bad437fee21438d702942a5db3ee1 100644 ---- a/src/main/java/net/minecraft/server/network/TextFilterClient.java -+++ b/src/main/java/net/minecraft/server/network/TextFilterClient.java -@@ -32,6 +32,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.concurrent.AsyncExecutor; - import org.slf4j.Logger; - - public class TextFilterClient implements AutoCloseable { -@@ -62,7 +63,7 @@ public class TextFilterClient implements AutoCloseable { - this.joinEncoder = joinEncoder; - this.leaveEndpoint = leaveEndpoint; - this.leaveEncoder = leaveEncoder; -- this.workerPool = Executors.newFixedThreadPool(parallelism, THREAD_FACTORY); -+ this.workerPool = AsyncExecutor.instance; // Gale - centralized async execution - remove text filter executor - } - - private static URL getEndpoint(URI root, @Nullable JsonObject endpoints, String key, String fallback) throws MalformedURLException {