From 15237645d51b8954cd3ac7dadbf89a375114debd Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 5 Apr 2025 16:30:48 +0200 Subject: [PATCH 01/27] fix a regression caused by advencement fixing for PWT --- ...-SparklyPaper-Parallel-world-ticking.patch | 157 ++++++++++++------ 1 file changed, 102 insertions(+), 55 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0139-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0139-SparklyPaper-Parallel-world-ticking.patch index 7a21b0cb..6d404852 100644 --- a/leaf-server/minecraft-patches/features/0139-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0139-SparklyPaper-Parallel-world-ticking.patch @@ -424,7 +424,7 @@ index c50a301a0c2365c2052aefc6a23fcf6fa82e1b9d..ac751d460ae0c8dbb858c4047c459a11 } // CraftBukkit end diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java -index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..8b74e9abba806a311f52b82732ce3c92638d50c4 100644 +index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..a7e1c9979897a12a7a8f417545ae96f703a1b248 100644 --- a/net/minecraft/server/PlayerAdvancements.java +++ b/net/minecraft/server/PlayerAdvancements.java @@ -19,6 +19,7 @@ import java.nio.file.Path; @@ -435,94 +435,141 @@ index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..8b74e9abba806a311f52b82732ce3c92 import java.util.Map; import java.util.Set; import java.util.Map.Entry; -@@ -53,7 +54,8 @@ public class PlayerAdvancements { +@@ -53,8 +54,9 @@ public class PlayerAdvancements { private AdvancementTree tree; private final Map progress = new LinkedHashMap<>(); private final Set visible = new HashSet<>(); - private final Set progressChanged = new HashSet<>(); -+ private final Set progressChanged = new HashSet<>(); // Default implementation -+ private final Set progressChangedConcurrent = ConcurrentHashMap.newKeySet(); // Thread-safe implementation for parallel world ticking - private final Set rootsToUpdate = new HashSet<>(); +- private final Set rootsToUpdate = new HashSet<>(); ++ private final Set progressChanged = new HashSet<>(); // Used when PWT is disabled ++ private final Set progressChangedConcurrent = ConcurrentHashMap.newKeySet(); // Used when PWT is enabled ++ private final Set rootsToUpdate = new HashSet<>(); // Always managed on player tick thread private ServerPlayer player; @Nullable -@@ -184,7 +186,13 @@ public class PlayerAdvancements { + private AdvancementHolder lastSelectedTab; +@@ -88,6 +90,8 @@ public class PlayerAdvancements { + this.visible.clear(); + this.rootsToUpdate.clear(); + this.progressChanged.clear(); ++ // PWT Fix: Also clear concurrent set on reload ++ this.progressChangedConcurrent.clear(); + this.isFirstPacket = true; + this.lastSelectedTab = null; + this.tree = manager.tree(); +@@ -151,6 +155,7 @@ public class PlayerAdvancements { + if (org.galemc.gale.configuration.GaleGlobalConfiguration.get().logToConsole.ignoredAdvancements) LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", path, this.playerSavePath); // Gale - Purpur - do not log ignored advancements + } else { + this.startProgress(advancementHolder, progress); ++ // PWT Fix: Always add to non-concurrent set during load, flushDirty will handle sync + this.progressChanged.add(advancementHolder); + this.markForVisibilityUpdate(advancementHolder); + } +@@ -183,25 +188,25 @@ public class PlayerAdvancements { + return false; } // Paper end - Add PlayerAdvancementCriterionGrantEvent - this.unregisterListeners(advancement); +- this.unregisterListeners(advancement); - this.progressChanged.add(advancement); -+ -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { -+ this.progressChangedConcurrent.add(advancement); -+ } else { -+ this.progressChanged.add(advancement); -+ } -+ - flag = true; - if (!isDone && orStartProgress.isDone()) { +- flag = true; +- if (!isDone && orStartProgress.isDone()) { ++ this.unregisterListeners(advancement); // Must unregister criteria listeners ++ (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.progressChangedConcurrent : this.progressChanged).add(advancement); ++ flag = true; // Mark progress changed ++ if (!isDone && orStartProgress.isDone()) { // If the advancement was just completed // Paper start - Add Adventure message to PlayerAdvancementDoneEvent -@@ -221,7 +229,11 @@ public class PlayerAdvancements { +- final net.kyori.adventure.text.Component message = advancement.value().display().flatMap(info -> { +- return java.util.Optional.ofNullable( +- info.shouldAnnounceChat() ? io.papermc.paper.adventure.PaperAdventure.asAdventure(info.getType().createAnnouncement(advancement, this.player)) : null +- ); +- }).orElse(null); +- final org.bukkit.event.player.PlayerAdvancementDoneEvent event = new org.bukkit.event.player.PlayerAdvancementDoneEvent(this.player.getBukkitEntity(), advancement.toBukkit(), message); ++ final net.kyori.adventure.text.Component message = advancement.value().display().flatMap(info -> { // Paper - Add Adventure message to PlayerAdvancementDoneEvent ++ return java.util.Optional.ofNullable( // Paper - Add Adventure message to PlayerAdvancementDoneEvent ++ info.shouldAnnounceChat() ? io.papermc.paper.adventure.PaperAdventure.asAdventure(info.getType().createAnnouncement(advancement, this.player)) : null // Paper - Add Adventure message to PlayerAdvancementDoneEvent ++ ); // Paper - Add Adventure message to PlayerAdvancementDoneEvent ++ }).orElse(null); // Paper - Add Adventure message to PlayerAdvancementDoneEvent ++ final org.bukkit.event.player.PlayerAdvancementDoneEvent event = new org.bukkit.event.player.PlayerAdvancementDoneEvent(this.player.getBukkitEntity(), advancement.toBukkit(), message); // Paper - Add Adventure message to PlayerAdvancementDoneEvent + this.player.level().getCraftServer().getPluginManager().callEvent(event); // CraftBukkit + // Paper end + advancement.value().rewards().grant(this.player); + advancement.value().display().ifPresent(displayInfo -> { + // Paper start - Add Adventure message to PlayerAdvancementDoneEvent +- if (event.message() != null && this.player.serverLevel().getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) { +- if (org.purpurmc.purpur.PurpurConfig.advancementOnlyBroadcastToAffectedPlayer) this.player.sendMessage(message); else // Purpur - Configurable broadcast settings +- this.playerList.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false); ++ if (event.message() != null && this.player.serverLevel().getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) { // Paper - Add Adventure message to PlayerAdvancementDoneEvent ++ if (org.purpurmc.purpur.PurpurConfig.advancementOnlyBroadcastToAffectedPlayer) this.player.sendMessage(message); else // Purpur - Configurable broadcast settings // Paper - Add Adventure message to PlayerAdvancementDoneEvent ++ this.playerList.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false); // Paper - Add Adventure message to PlayerAdvancementDoneEvent + // Paper end + } + }); +@@ -220,12 +225,12 @@ public class PlayerAdvancements { + AdvancementProgress orStartProgress = this.getOrStartProgress(advancement); boolean isDone = orStartProgress.isDone(); if (orStartProgress.revokeProgress(criterionKey)) { - this.registerListeners(advancement); +- this.registerListeners(advancement); - this.progressChanged.add(advancement); -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { -+ this.progressChangedConcurrent.add(advancement); -+ } else { -+ this.progressChanged.add(advancement); -+ } ++ this.registerListeners(advancement); // Re-register listeners if it's no longer done ++ (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.progressChangedConcurrent : this.progressChanged).add(advancement); flag = true; } -@@ -271,7 +283,11 @@ public class PlayerAdvancements { +- if (isDone && !orStartProgress.isDone()) { ++ if (isDone && !orStartProgress.isDone()) { // If the advancement was just un-completed + this.markForVisibilityUpdate(advancement); + } + +@@ -271,7 +276,9 @@ public class PlayerAdvancements { } public void flushDirty(ServerPlayer serverPlayer) { - if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !this.progressChanged.isEmpty()) { -+ boolean hasProgressChanges = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled -+ ? !this.progressChangedConcurrent.isEmpty() -+ : !this.progressChanged.isEmpty(); -+ -+ if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || hasProgressChanges) { ++ final boolean useConcurrent = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled; ++ final Set relevantProgressSet = useConcurrent ? this.progressChangedConcurrent : this.progressChanged; ++ if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !relevantProgressSet.isEmpty()) { Map map = new HashMap<>(); Set set = new java.util.TreeSet<>(java.util.Comparator.comparing(adv -> adv.id().toString())); // Paper - Changed from HashSet to TreeSet ordered alphabetically. Set set1 = new HashSet<>(); -@@ -280,6 +296,19 @@ public class PlayerAdvancements { +@@ -279,16 +286,23 @@ public class PlayerAdvancements { + for (AdvancementNode advancementNode : this.rootsToUpdate) { this.updateTreeVisibility(advancementNode, set, set1); } ++ this.rootsToUpdate.clear(); // Roots processed, clear the set -+ // Process advancements with changed progress -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { -+ // Using concurrent set - create a copy to avoid possible ConcurrentModificationException -+ // during visualization and packet sending -+ Set copy = new HashSet<>(this.progressChangedConcurrent); -+ for (AdvancementHolder advancementHolder : copy) { -+ if (this.visible.contains(advancementHolder)) { +- this.rootsToUpdate.clear(); ++ if (!relevantProgressSet.isEmpty()) { ++ Set toProcess = useConcurrent ? new HashSet<>(relevantProgressSet) : relevantProgressSet; + +- for (AdvancementHolder advancementHolder : this.progressChanged) { +- if (this.visible.contains(advancementHolder)) { +- map.put(advancementHolder.id(), this.progress.get(advancementHolder)); ++ for (AdvancementHolder advancementHolder : toProcess) { ++ if (this.visible.contains(advancementHolder)) { // Only include progress for visible advancements + map.put(advancementHolder.id(), this.progress.get(advancementHolder)); + } + } +- } + +- this.progressChanged.clear(); ++ if (useConcurrent) { ++ this.progressChangedConcurrent.removeAll(toProcess); // Remove processed items from concurrent set ++ } else { ++ this.progressChanged.clear(); // Clear the regular set + } -+ this.progressChangedConcurrent.removeAll(copy); -+ } else { -+ // Original logic using non-concurrent set - this.rootsToUpdate.clear(); - - for (AdvancementHolder advancementHolder : this.progressChanged) { -@@ -289,6 +318,7 @@ public class PlayerAdvancements { - } - - this.progressChanged.clear(); + } if (!map.isEmpty() || !set.isEmpty() || !set1.isEmpty()) { serverPlayer.connection.send(new ClientboundUpdateAdvancementsPacket(this.isFirstPacket, set, set1, map)); } -@@ -334,6 +364,11 @@ public class PlayerAdvancements { - advancementOutput.add(advancementHolder); +@@ -331,9 +345,10 @@ public class PlayerAdvancements { + AdvancementHolder advancementHolder = node.holder(); + if (visible) { + if (this.visible.add(advancementHolder)) { +- advancementOutput.add(advancementHolder); ++ advancementOutput.add(advancementHolder); // Add to visible set for packet if (this.progress.containsKey(advancementHolder)) { - this.progressChanged.add(advancementHolder); -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { -+ this.progressChangedConcurrent.add(advancementHolder); -+ } else { -+ this.progressChanged.add(advancementHolder); -+ } +- this.progressChanged.add(advancementHolder); ++ // If progress exists, mark it changed so the progress data is sent ++ (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.progressChangedConcurrent : this.progressChanged).add(advancementHolder); } } } else if (this.visible.remove(advancementHolder)) { From 996427ed5d6cd597cbf2f2916bf246b1ad5500cc Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 5 Apr 2025 16:31:09 +0200 Subject: [PATCH 02/27] null handling on MultifaceSpreader (idk why this errors to begin with???) --- ...8-Null-handling-on-MultifaceSpreader.patch | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0158-Null-handling-on-MultifaceSpreader.patch diff --git a/leaf-server/minecraft-patches/features/0158-Null-handling-on-MultifaceSpreader.patch b/leaf-server/minecraft-patches/features/0158-Null-handling-on-MultifaceSpreader.patch new file mode 100644 index 00000000..5ca9d329 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0158-Null-handling-on-MultifaceSpreader.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Wed, 2 Apr 2025 23:03:22 +0200 +Subject: [PATCH] Null handling on MultifaceSpreader + +WHYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY + +diff --git a/net/minecraft/world/level/block/MultifaceSpreader.java b/net/minecraft/world/level/block/MultifaceSpreader.java +index 60f47334bb855d5216f57f888f131ba41f728d21..7d54362767ec2acfcb9f8f8703ec706a1c2fd03b 100644 +--- a/net/minecraft/world/level/block/MultifaceSpreader.java ++++ b/net/minecraft/world/level/block/MultifaceSpreader.java +@@ -10,6 +10,8 @@ import net.minecraft.world.level.BlockGetter; + import net.minecraft.world.level.LevelAccessor; + import net.minecraft.world.level.block.state.BlockState; + ++import static org.dreeam.leaf.config.LeafConfig.LOGGER; ++ + public class MultifaceSpreader { + public static final MultifaceSpreader.SpreadType[] DEFAULT_SPREAD_ORDER = new MultifaceSpreader.SpreadType[]{ + MultifaceSpreader.SpreadType.SAME_POSITION, MultifaceSpreader.SpreadType.SAME_PLANE, MultifaceSpreader.SpreadType.WRAP_AROUND +@@ -148,6 +150,12 @@ public class MultifaceSpreader { + } + + default boolean placeBlock(LevelAccessor level, MultifaceSpreader.SpreadPos pos, BlockState state, boolean markForPostprocessing) { ++ // Check for null ++ if (pos.source() == null || pos.pos() == null) { ++ LOGGER.warn("Invalid SpreadPos with null source or position: {}", pos); ++ return false; ++ } ++ + BlockState stateForPlacement = this.getStateForPlacement(state, level, pos.pos(), pos.face()); + if (stateForPlacement != null) { + if (markForPostprocessing) { From d004ce16ff1a1837a9fdb259ec271028587cb3b2 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sun, 6 Apr 2025 04:18:47 -0400 Subject: [PATCH 03/27] Fix connection message parse Move name parse before minimessage component deserialize, to prevent failed show connection message which has name placeholder in the minimessage string --- .../0054-Configurable-connection-message.patch | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch b/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch index 9a1aea24..34bfce8d 100644 --- a/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch +++ b/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configurable connection message diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index e8683f45823cac55e3e68ccc500f10f0632e72fd..c9a3dd3e2b17ef8d5457766bdc2bea19a1948426 100644 +index e8683f45823cac55e3e68ccc500f10f0632e72fd..5b686f6f5016fefca1e2547ee1ab6c6235aedb29 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -434,7 +434,7 @@ public abstract class PlayerList { @@ -35,7 +35,7 @@ index e8683f45823cac55e3e68ccc500f10f0632e72fd..c9a3dd3e2b17ef8d5457766bdc2bea19 this.cserver.getPluginManager().callEvent(playerQuitEvent); player.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage()); -@@ -1673,4 +1673,34 @@ public abstract class PlayerList { +@@ -1673,4 +1673,38 @@ public abstract class PlayerList { public boolean isAllowCommandsForAllPlayers() { return this.allowCommandsForAllPlayers; } @@ -47,8 +47,10 @@ index e8683f45823cac55e3e68ccc500f10f0632e72fd..c9a3dd3e2b17ef8d5457766bdc2bea19 + return io.papermc.paper.adventure.PaperAdventure.asAdventure(defaultJoinMsg); + } + -+ return net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.dreeam.leaf.config.modules.misc.ConnectionMessage.joinMessage) -+ .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("").replacement(craftPlayer.getName()).build()) ++ final String joinMessage = org.dreeam.leaf.config.modules.misc.ConnectionMessage.joinMessage ++ .replace("", craftPlayer.getName()); ++ ++ return net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(joinMessage) + .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("").replacement(craftPlayer.displayName()).build()); + } + @@ -61,8 +63,10 @@ index e8683f45823cac55e3e68ccc500f10f0632e72fd..c9a3dd3e2b17ef8d5457766bdc2bea19 + return defaultJoinMsg; + } + ++ final String quitMessage = org.dreeam.leaf.config.modules.misc.ConnectionMessage.quitMessage ++ .replace("", craftPlayer.getName()); ++ + return net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.dreeam.leaf.config.modules.misc.ConnectionMessage.quitMessage) -+ .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("").replacement(craftPlayer.getName()).build()) + .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("").replacement(craftPlayer.displayName()).build()); + } + From 4cd885a8c21e6cb810382990282c98b68cdd9339 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 6 Apr 2025 14:44:44 +0200 Subject: [PATCH 04/27] more virtual thread options --- .../features/0159-More-virtual-threads.patch | 139 ++++++++++++++++++ .../config/modules/opt/VT4DownloadPool.java | 21 +++ .../modules/opt/VT4ProfileExecutor.java | 21 +++ 3 files changed, 181 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0159-More-virtual-threads.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4DownloadPool.java create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4ProfileExecutor.java diff --git a/leaf-server/minecraft-patches/features/0159-More-virtual-threads.patch b/leaf-server/minecraft-patches/features/0159-More-virtual-threads.patch new file mode 100644 index 00000000..b3a142b6 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0159-More-virtual-threads.patch @@ -0,0 +1,139 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sun, 6 Apr 2025 11:22:35 +0200 +Subject: [PATCH] More virtual threads + + +diff --git a/net/minecraft/Util.java b/net/minecraft/Util.java +index b097f685e826e70008e3a096ee5f1d4fccf25680..f374428c65e929b8795f2a0ddfe3137b4afca9bc 100644 +--- a/net/minecraft/Util.java ++++ b/net/minecraft/Util.java +@@ -61,6 +61,7 @@ import java.util.concurrent.ForkJoinPool; + import java.util.concurrent.ForkJoinWorkerThread; + import java.util.concurrent.LinkedBlockingQueue; + import java.util.concurrent.TimeUnit; ++import java.util.concurrent.ThreadFactory; + import java.util.concurrent.atomic.AtomicInteger; + import java.util.function.BiFunction; + import java.util.function.BooleanSupplier; +@@ -86,6 +87,10 @@ import net.minecraft.util.TimeSource; + import net.minecraft.util.datafix.DataFixers; + import net.minecraft.world.level.block.state.properties.Property; + import org.slf4j.Logger; ++import org.galemc.gale.virtualthread.VirtualThreadService; // Gale - virtual thread support ++import org.dreeam.leaf.config.modules.opt.VT4DownloadPool; ++import org.dreeam.leaf.config.modules.opt.VT4ProfileExecutor; ++ + + public class Util { + static final Logger LOGGER = LogUtils.getLogger(); +@@ -97,7 +102,7 @@ public class Util { + public static final TracingExecutor DIMENSION_DATA_IO_POOL = makeExtraIoExecutor("Dimension-Data-IO-Worker-"); // Paper - Separate dimension data IO pool + private static final TracingExecutor DOWNLOAD_POOL = makeIoExecutor("Download-", true); + // 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() { ++ public static final ExecutorService PROFILE_EXECUTOR = createProfileExecutor(); /* new Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { + + private final AtomicInteger count = new AtomicInteger(); + +@@ -110,7 +115,27 @@ public class Util { + }); + return ret; + } +- }); ++ }); */ ++ private static ExecutorService createProfileExecutor() { ++ final ThreadFactory factory; ++ if (VT4ProfileExecutor.enabled && VirtualThreadService.isSupported()) { ++ factory = VirtualThreadService.get().createFactory(); ++ } else { ++ factory = new ThreadFactory() { ++ private final AtomicInteger count = new AtomicInteger(); ++ @Override ++ public Thread newThread(Runnable run) { ++ Thread ret = new Thread(run); ++ ret.setName("Profile Lookup Executor #" + this.count.getAndIncrement()); ++ ret.setUncaughtExceptionHandler((Thread thread, Throwable throwable) -> { ++ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable); ++ }); ++ return ret; ++ } ++ }; ++ } ++ return Executors.newFixedThreadPool(2, factory); ++ } + // Paper end - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread + private static final DateTimeFormatter FILENAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT); + public static final int LINEAR_LOOKUP_THRESHOLD = 8; +@@ -254,16 +279,29 @@ public class Util { + } + + private static TracingExecutor makeIoExecutor(String name, boolean daemon) { +- AtomicInteger atomicInteger = new AtomicInteger(1); +- return new TracingExecutor(Executors.newCachedThreadPool(runnable -> { +- Thread thread = new Thread(runnable); +- String string = name + atomicInteger.getAndIncrement(); +- TracyClient.setThreadName(string, name.hashCode()); +- thread.setName(string); +- thread.setDaemon(daemon); +- thread.setUncaughtExceptionHandler(Util::onThreadException); +- return thread; +- })); ++ final ThreadFactory factory; ++ final boolean useVirtualThreads; // Gale - virtual thread support ++ if (name.startsWith("Download-")) { // Gale - virtual thread support ++ useVirtualThreads = VT4DownloadPool.enabled && VirtualThreadService.isSupported(); // Gale - virtual thread support ++ } else { ++ useVirtualThreads = false; ++ } ++ ++ if (useVirtualThreads) { ++ factory = VirtualThreadService.get().createFactory(); ++ } else { ++ AtomicInteger atomicInteger = new AtomicInteger(1); ++ factory = runnable -> { ++ Thread thread = new Thread(runnable); ++ String string = name + atomicInteger.getAndIncrement(); ++ TracyClient.setThreadName(string, name.hashCode()); ++ thread.setName(string); ++ thread.setDaemon(daemon); ++ thread.setUncaughtExceptionHandler(Util::onThreadException); ++ return thread; ++ }; ++ } ++ return new TracingExecutor(Executors.newCachedThreadPool(factory)); + } + + // Paper start - Separate dimension data IO pool +@@ -1099,7 +1137,7 @@ public class Util { + } + + public static Typed readTypedOrThrow(Type type, Dynamic data, boolean partial) { +- DataResult> dataResult = type.readTyped(data).map(Pair::getFirst); ++ DataResult> dataResult = type.readTyped(data).map(Pair::getFirst); // Paper - Fix generics issue // Gale - Fix generics issue + + try { + return partial ? dataResult.getPartialOrThrow(IllegalStateException::new) : dataResult.getOrThrow(IllegalStateException::new); +@@ -1143,12 +1181,14 @@ public class Util { + + private final String telemetryName; + ++ // Paper start - Fix warnings on build by removing client-only code + OS(final String telemetryName) { + this.telemetryName = telemetryName; + } + + public void openUri(URI uri) { +- throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code ++ // This method is not useful on dedicated servers. ++ // throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code + } + + public void openFile(File file) { +@@ -1179,5 +1219,6 @@ public class Util { + public String telemetryName() { + return this.telemetryName; + } ++ // Paper end - Fix warnings on build by removing client-only code + } + } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4DownloadPool.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4DownloadPool.java new file mode 100644 index 00000000..22fb0741 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4DownloadPool.java @@ -0,0 +1,21 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class VT4DownloadPool extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName(); + } + + public static boolean enabled = true; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath() + ".use-virtual-thread-for-download-pool", enabled, + config.pickStringRegionBased( + "Use the new Virtual Thread introduced in JDK 21 for download worker pool.", + "是否为下载工作线程池使用虚拟线程(如果可用)。")); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4ProfileExecutor.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4ProfileExecutor.java new file mode 100644 index 00000000..34c04e56 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4ProfileExecutor.java @@ -0,0 +1,21 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class VT4ProfileExecutor extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName(); + } + + public static boolean enabled = true; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath() + ".use-virtual-thread-for-profile-executor", enabled, + config.pickStringRegionBased( + "Use the new Virtual Thread introduced in JDK 21 for profile lookup executor.", + "是否为档案查询执行器使用虚拟线程(如果可用)。")); + } +} From fbb040e9306bbc9875fbb2a977b5b25f3c4f9fc3 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sun, 6 Apr 2025 14:43:38 -0400 Subject: [PATCH 05/27] Fix --- .../features/0054-Configurable-connection-message.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch b/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch index 34bfce8d..529a69d4 100644 --- a/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch +++ b/leaf-server/minecraft-patches/features/0054-Configurable-connection-message.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configurable connection message diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index e8683f45823cac55e3e68ccc500f10f0632e72fd..5b686f6f5016fefca1e2547ee1ab6c6235aedb29 100644 +index e8683f45823cac55e3e68ccc500f10f0632e72fd..5c88d51920481235145bb7fd8cf148607b2dbed0 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -434,7 +434,7 @@ public abstract class PlayerList { @@ -66,7 +66,7 @@ index e8683f45823cac55e3e68ccc500f10f0632e72fd..5b686f6f5016fefca1e2547ee1ab6c62 + final String quitMessage = org.dreeam.leaf.config.modules.misc.ConnectionMessage.quitMessage + .replace("", craftPlayer.getName()); + -+ return net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.dreeam.leaf.config.modules.misc.ConnectionMessage.quitMessage) ++ return net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(quitMessage) + .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("").replacement(craftPlayer.displayName()).build()); + } + From 4363934dc9981a1bdf7dfc9f6b27683466cf71be Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sun, 6 Apr 2025 20:08:28 -0400 Subject: [PATCH 06/27] Fix NPE --- .../features/0085-Multithreaded-Tracker.patch | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index 162e665e..ea4b7ede 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -218,7 +218,7 @@ index d8298c7925e3bcea07ead4d438478cc51abcfa16..75670751064add901c2628d53d802835 attributesToSync.clear(); diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 0290e1f0c45677d337f77a0c8269894b32a43ca9..268c463d379528b8242f1628e97e67ea638b7ced 100644 +index 0290e1f0c45677d337f77a0c8269894b32a43ca9..df7e7833890fab9fd63afa93903f2c30abbfd520 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -2496,7 +2496,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -230,8 +230,17 @@ index 0290e1f0c45677d337f77a0c8269894b32a43ca9..268c463d379528b8242f1628e97e67ea return this.moonrise$getEntityLookup(); // Paper - rewrite chunk system } +@@ -2728,7 +2728,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + + map.carriedByPlayers.remove(player); +- if (map.carriedBy.removeIf(holdingPlayer -> holdingPlayer.player == player)) { ++ if (map.carriedBy.removeIf(holdingPlayer -> holdingPlayer != null && holdingPlayer.player == player)) { // Leaf - Multithreaded tracker + map.decorations.remove(player.getName().getString()); + } + } diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index c30e017e6cffa6aa828b0f6e8889885dbaaa4680..ba84b1caab62e3f04f2f9e7aed0c659c0106bc29 100644 +index 04bf8bba0d8c0d5459605253dcc3f135bf43fd95..abe79d07196de0a10a382d4c37161c7eb4a604ae 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -1819,7 +1819,7 @@ public class ServerGamePacketListenerImpl @@ -283,3 +292,15 @@ index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..371dd51c62c9a109014851c8a1562a5c private final AttributeSupplier supplier; private final java.util.function.Function, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables +diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java +index 681dec447486138088fe5f705ef4fadab531139f..27f8a22d798a17dbd5949d1b6ff0526837fe91d5 100644 +--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java ++++ b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java +@@ -279,6 +279,7 @@ public class MapItemSavedData extends SavedData { + + for (int i = 0; i < this.carriedBy.size(); i++) { + MapItemSavedData.HoldingPlayer holdingPlayer1 = this.carriedBy.get(i); ++ if (holdingPlayer1 == null) continue; // Leaf - Multithreaded tracker + Player player1 = holdingPlayer1.player; + String string = player1.getName().getString(); + if (!player1.isRemoved() && (player1.getInventory().contains(predicate) || mapStack.isFramed())) { From 1a64b379a682cdfd666e2581ed7942069323de21 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Mon, 7 Apr 2025 20:00:12 -0400 Subject: [PATCH 07/27] Fix version fetcher --- .../main/java/org/dreeam/leaf/version/LeafVersionFetcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/version/LeafVersionFetcher.java b/leaf-server/src/main/java/org/dreeam/leaf/version/LeafVersionFetcher.java index 861a7626..496201a0 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/version/LeafVersionFetcher.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/version/LeafVersionFetcher.java @@ -6,7 +6,7 @@ public class LeafVersionFetcher extends AbstractPaperVersionFetcher { public LeafVersionFetcher() { super( - "ver/1.21.3", + "ver/1.21.4", "https://github.com/Winds-Studio/Leaf", "Winds-Studio", "Leaf", From 339bff6a8a6fc2196e3ff630cfa484a59f7dd246 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Tue, 8 Apr 2025 14:39:30 -0400 Subject: [PATCH 08/27] Fix plugin compatibility Fix checking whether inventory owner is player in calling PlayerInventoryOverflowEvent --- .../features/0030-PlayerInventoryOverflowEvent.patch | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/leaf-server/paper-patches/features/0030-PlayerInventoryOverflowEvent.patch b/leaf-server/paper-patches/features/0030-PlayerInventoryOverflowEvent.patch index 7fdd280d..6378bbb5 100644 --- a/leaf-server/paper-patches/features/0030-PlayerInventoryOverflowEvent.patch +++ b/leaf-server/paper-patches/features/0030-PlayerInventoryOverflowEvent.patch @@ -5,17 +5,18 @@ Subject: [PATCH] PlayerInventoryOverflowEvent diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java -index 8b4f8a475faafe3b8a479160888145c4aa603a27..e97bb84d976229ba0d386efbade71be7347d0a1a 100644 +index 8b4f8a475faafe3b8a479160888145c4aa603a27..6b0067e83920d32c62416a0c3d8ef2940ca7ed2b 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java -@@ -340,6 +340,15 @@ public class CraftInventory implements Inventory { +@@ -340,6 +340,16 @@ public class CraftInventory implements Inventory { } } } + + // Leaf start - PlayerInventoryOverflowEvent -+ if (org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.getHandlerList().getRegisteredListeners().length > 0 && !leftover.isEmpty() && this.getHolder() instanceof org.bukkit.craftbukkit.entity.CraftPlayer craftPlayer) { -+ new org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent(craftPlayer, leftover).callEvent(); ++ if (org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.getHandlerList().getRegisteredListeners().length > 0 ++ && !leftover.isEmpty() && this.inventory instanceof net.minecraft.world.entity.player.Inventory && this.inventory.getOwner() instanceof org.bukkit.entity.Player player) { ++ new org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent(player, leftover).callEvent(); + + leftover = new HashMap<>(); + } From 25684977bb6870d19776ab80a8b137e0d00360f7 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Thu, 10 Apr 2025 01:45:46 -0400 Subject: [PATCH 09/27] Updated Upstream (Paper/Gale) Upstream has released updates that appear to apply and compile correctly Paper Changes: PaperMC/Paper@0cf73158 Bump to adventure 4.20.0 (#12391) Gale Changes: Dreeam-qwq/Gale@966b5c68 Updated Upstream (Paper) --- gradle.properties | 2 +- leaf-api/build.gradle.kts.patch | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 241a164a..93e7b98e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ group=cn.dreeam.leaf mcVersion=1.21.4 version=1.21.4-R0.1-SNAPSHOT -galeCommit=ffc98037e08d5bffb7a5d1a5ca277c5234dc089b +galeCommit=966b5c6859a109c0cf28b09527748dc68a79e37f org.gradle.configuration-cache=true org.gradle.caching=true diff --git a/leaf-api/build.gradle.kts.patch b/leaf-api/build.gradle.kts.patch index 6cbedb8c..ed47c8b0 100644 --- a/leaf-api/build.gradle.kts.patch +++ b/leaf-api/build.gradle.kts.patch @@ -7,7 +7,7 @@ -val annotationsVersion = "26.0.1" +val annotationsVersion = "26.0.2" // Leaf - Bump Dependencies val bungeeCordChatVersion = "1.20-R0.2" - val adventureVersion = "4.18.0" + val adventureVersion = "4.20.0" -val slf4jVersion = "2.0.9" -val log4jVersion = "2.17.1" +// Leaf start - Bump Dependencies From d31559d60b5bb42f80cd03bf2d2c1cd471b77029 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 10 Apr 2025 15:05:59 +0200 Subject: [PATCH 10/27] i pushed this but need someone to test before and after for sake of testing --- ...fectsFromBlocks-and-checkInsideBlock.patch | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0160-Optimize-applyEffectsFromBlocks-and-checkInsideBlock.patch diff --git a/leaf-server/minecraft-patches/features/0160-Optimize-applyEffectsFromBlocks-and-checkInsideBlock.patch b/leaf-server/minecraft-patches/features/0160-Optimize-applyEffectsFromBlocks-and-checkInsideBlock.patch new file mode 100644 index 00000000..0ac1f020 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0160-Optimize-applyEffectsFromBlocks-and-checkInsideBlock.patch @@ -0,0 +1,246 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Wed, 9 Apr 2025 18:46:23 +0200 +Subject: [PATCH] Optimize applyEffectsFromBlocks and checkInsideBlocks + + +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 075fcbcde23b5bb7b27ff622e8d188c3a2583973..3a791927fa33be4346f28b55325a0340445731ae 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -9,6 +9,8 @@ import com.mojang.logging.LogUtils; + import it.unimi.dsi.fastutil.floats.FloatArraySet; + import it.unimi.dsi.fastutil.floats.FloatArrays; + import it.unimi.dsi.fastutil.floats.FloatSet; ++import it.unimi.dsi.fastutil.ints.Int2ObjectMap; ++import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + import it.unimi.dsi.fastutil.longs.LongOpenHashSet; + import it.unimi.dsi.fastutil.longs.LongSet; + import it.unimi.dsi.fastutil.objects.Object2DoubleArrayMap; +@@ -832,7 +834,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + protected AABB makeBoundingBox(Vec3 position) { +- return this.dimensions.makeBoundingBox(position); ++ if (this.lastDimensions == null || this.lastDimensions.width() != this.dimensions.width() || this.lastDimensions.height() != this.dimensions.height()) { ++ this.lastDimensions = this.dimensions; ++ this.cachedBoundingBox = this.dimensions.makeBoundingBox(Vec3.ZERO); ++ } ++ ++ return this.cachedBoundingBox.move(position); + } + + protected void reapplyPosition() { +@@ -1351,33 +1358,56 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + public void applyEffectsFromBlocks(Vec3 oldPosition, Vec3 position) { +- if (this.isAffectedByBlocks()) { +- if (this.onGround()) { +- BlockPos onPosLegacy = this.getOnPosLegacy(); ++ if (!this.isAffectedByBlocks()) { ++ return; ++ } ++ ++ if (this.onGround()) { ++ BlockPos onPosLegacy = this.getOnPosLegacy(); ++ if (this.cachedSupportingBlockState == null || !this.isSupportedBy(onPosLegacy)) { + BlockState blockState = this.level().getBlockState(onPosLegacy); + blockState.getBlock().stepOn(this.level(), onPosLegacy, blockState, this); ++ this.cachedSupportingBlockState = blockState; ++ } else { ++ this.cachedSupportingBlockState.getBlock().stepOn(this.level(), onPosLegacy, this.cachedSupportingBlockState, this); + } ++ } + ++ if (oldPosition.distanceToSqr(position) > 1.0E-8) { + this.movementThisTick.add(new Entity.Movement(oldPosition, position)); +- List list = List.copyOf(this.movementThisTick); +- this.movementThisTick.clear(); +- this.checkInsideBlocks(list, this.blocksInside); +- boolean flag = Iterables.any(this.blocksInside, state -> state.is(BlockTags.FIRE) || state.is(Blocks.LAVA)); +- this.blocksInside.clear(); +- if (!flag && this.isAlive()) { +- if (this.remainingFireTicks <= 0) { +- this.setRemainingFireTicks(-this.getFireImmuneTicks()); +- } ++ } + +- if (this.wasOnFire && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { +- this.playEntityOnFireExtinguishedSound(); +- } ++ if (this.movementThisTick.isEmpty()) { ++ return; ++ } ++ ++ this.checkInsideBlocks(this.movementThisTick, this.blocksInside); ++ ++ boolean inFireOrLava = false; ++ for (BlockState state : this.blocksInside) { ++ if (state.is(BlockTags.FIRE) || state.is(Blocks.LAVA)) { ++ inFireOrLava = true; ++ break; + } ++ } ++ ++ this.movementThisTick.clear(); + +- if (this.isOnFire() && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { ++ if (!inFireOrLava && this.isAlive()) { ++ if (this.remainingFireTicks <= 0) { + this.setRemainingFireTicks(-this.getFireImmuneTicks()); + } ++ ++ if (this.wasOnFire && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { ++ this.playEntityOnFireExtinguishedSound(); ++ } + } ++ ++ if (this.isOnFire() && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { ++ this.setRemainingFireTicks(-this.getFireImmuneTicks()); ++ } ++ ++ this.blocksInside.clear(); + } + + public boolean isAffectedByBlocks() { +@@ -1706,50 +1736,109 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + public void recordMovementThroughBlocks(Vec3 oldPosition, Vec3 position) { + this.movementThisTick.add(new Entity.Movement(oldPosition, position)); + } ++ private final Int2ObjectMap blockStateCache = new Int2ObjectOpenHashMap<>(16); ++ private BlockPos lastBlockPos = BlockPos.ZERO; ++ private BlockState lastBlockState = null; + + private void checkInsideBlocks(List movements, Set blocksInside) { +- if (this.isAffectedByBlocks()) { +- LongSet set = this.visitedBlocks; ++ if (!this.isAffectedByBlocks()) { ++ return; ++ } ++ ++ this.visitedBlocks.clear(); ++ this.blockStateCache.clear(); ++ ++ for (Entity.Movement movement : movements) { ++ Vec3 fromPos = movement.from(); ++ Vec3 toPos = movement.to(); + +- for (Entity.Movement movement : movements) { +- Vec3 vec3 = movement.from(); +- Vec3 vec31 = movement.to(); +- AABB aabb = this.makeBoundingBox(vec31).deflate(1.0E-5F); ++ if (fromPos.distanceToSqr(toPos) < 1.0E-8) { ++ continue; ++ } ++ ++ AABB aabb = this.makeBoundingBox(toPos).deflate(1.0E-5F); ++ ++ int minX = Mth.floor(Math.min(fromPos.x, toPos.x) - aabb.getXsize()/2); ++ int maxX = Mth.ceil(Math.max(fromPos.x, toPos.x) + aabb.getXsize()/2); ++ int minY = Mth.floor(Math.min(fromPos.y, toPos.y) - aabb.getYsize()/2); ++ int maxY = Mth.ceil(Math.max(fromPos.y, toPos.y) + aabb.getYsize()/2); ++ int minZ = Mth.floor(Math.min(fromPos.z, toPos.z) - aabb.getZsize()/2); ++ int maxZ = Mth.ceil(Math.max(fromPos.z, toPos.z) + aabb.getZsize()/2); ++ ++ int minChunkX = minX >> 4; ++ int maxChunkX = maxX >> 4; ++ int minChunkZ = minZ >> 4; ++ int maxChunkZ = maxZ >> 4; + +- for (BlockPos blockPos : BlockGetter.boxTraverseBlocks(vec3, vec31, aabb)) { +- if (!this.isAlive()) { +- return; ++ BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); ++ ++ for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) { ++ for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ++) { ++ if (!level().hasChunkAt(chunkX, chunkZ)) { ++ continue; + } + +- BlockState blockState = this.level().getBlockState(blockPos); +- if (!blockState.isAir() && set.add(blockPos.asLong())) { +- try { +- VoxelShape entityInsideCollisionShape = blockState.getEntityInsideCollisionShape(this.level(), blockPos); +- if (entityInsideCollisionShape != Shapes.block() +- && !this.collidedWithShapeMovingFrom(vec3, vec31, blockPos, entityInsideCollisionShape)) { +- continue; +- } ++ int xStart = Math.max(minX, chunkX << 4); ++ int xEnd = Math.min(maxX, (chunkX << 4) + 15); ++ int zStart = Math.max(minZ, chunkZ << 4); ++ int zEnd = Math.min(maxZ, (chunkZ << 4) + 15); + +- blockState.entityInside(this.level(), blockPos, this); +- this.onInsideBlock(blockState); +- } catch (Throwable var16) { +- CrashReport crashReport = CrashReport.forThrowable(var16, "Colliding entity with block"); +- CrashReportCategory crashReportCategory = crashReport.addCategory("Block being collided with"); +- CrashReportCategory.populateBlockDetails(crashReportCategory, this.level(), blockPos, blockState); +- CrashReportCategory crashReportCategory1 = crashReport.addCategory("Entity being checked for collision"); +- this.fillCrashReportCategory(crashReportCategory1); +- throw new ReportedException(crashReport); +- } ++ for (int x = xStart; x <= xEnd; x++) { ++ for (int z = zStart; z <= zEnd; z++) { ++ for (int y = minY; y <= maxY; y++) { ++ if (!this.isAlive()) { ++ return; ++ } ++ ++ mutablePos.set(x, y, z); ++ long posLong = mutablePos.asLong(); ++ ++ if (!this.visitedBlocks.add(posLong)) { ++ continue; ++ } + +- blocksInside.add(blockState); ++ BlockState blockState; ++ if (mutablePos.equals(this.lastBlockPos)) { ++ blockState = this.lastBlockState; ++ } else { ++ blockState = this.level().getBlockState(mutablePos); ++ this.lastBlockPos = mutablePos.immutable(); ++ this.lastBlockState = blockState; ++ } ++ ++ if (blockState.isAir()) { ++ continue; ++ } ++ ++ try { ++ VoxelShape entityInsideCollisionShape = blockState.getEntityInsideCollisionShape(this.level(), mutablePos); ++ if (entityInsideCollisionShape != Shapes.block() && ++ !this.collidedWithShapeMovingFrom(fromPos, toPos, mutablePos, entityInsideCollisionShape)) { ++ continue; ++ } ++ ++ blockState.entityInside(this.level(), mutablePos, this); ++ this.onInsideBlock(blockState); ++ blocksInside.add(blockState); ++ } catch (Throwable var16) { ++ CrashReport crashReport = CrashReport.forThrowable(var16, "Colliding entity with block"); ++ CrashReportCategory crashReportCategory = crashReport.addCategory("Block being collided with"); ++ CrashReportCategory.populateBlockDetails(crashReportCategory, this.level(), mutablePos, blockState); ++ CrashReportCategory crashReportCategory1 = crashReport.addCategory("Entity being checked for collision"); ++ this.fillCrashReportCategory(crashReportCategory1); ++ throw new ReportedException(crashReport); ++ } ++ } ++ } + } + } + } +- +- set.clear(); + } + } + ++ private EntityDimensions lastDimensions = null; ++ private AABB cachedBoundingBox = null; ++ + private boolean collidedWithShapeMovingFrom(Vec3 oldPosition, Vec3 position, BlockPos pos, VoxelShape shape) { + AABB aabb = this.makeBoundingBox(oldPosition); + Vec3 vec3 = position.subtract(oldPosition); From 255433caba165be33da16c8427e52b19b6af3d1f Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Fri, 11 Apr 2025 10:52:50 +0200 Subject: [PATCH 11/27] time to think something else --- ...fectsFromBlocks-and-checkInsideBlock.patch | 246 ------------------ 1 file changed, 246 deletions(-) delete mode 100644 leaf-server/minecraft-patches/features/0160-Optimize-applyEffectsFromBlocks-and-checkInsideBlock.patch diff --git a/leaf-server/minecraft-patches/features/0160-Optimize-applyEffectsFromBlocks-and-checkInsideBlock.patch b/leaf-server/minecraft-patches/features/0160-Optimize-applyEffectsFromBlocks-and-checkInsideBlock.patch deleted file mode 100644 index 0ac1f020..00000000 --- a/leaf-server/minecraft-patches/features/0160-Optimize-applyEffectsFromBlocks-and-checkInsideBlock.patch +++ /dev/null @@ -1,246 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Wed, 9 Apr 2025 18:46:23 +0200 -Subject: [PATCH] Optimize applyEffectsFromBlocks and checkInsideBlocks - - -diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 075fcbcde23b5bb7b27ff622e8d188c3a2583973..3a791927fa33be4346f28b55325a0340445731ae 100644 ---- a/net/minecraft/world/entity/Entity.java -+++ b/net/minecraft/world/entity/Entity.java -@@ -9,6 +9,8 @@ import com.mojang.logging.LogUtils; - import it.unimi.dsi.fastutil.floats.FloatArraySet; - import it.unimi.dsi.fastutil.floats.FloatArrays; - import it.unimi.dsi.fastutil.floats.FloatSet; -+import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; - import it.unimi.dsi.fastutil.longs.LongOpenHashSet; - import it.unimi.dsi.fastutil.longs.LongSet; - import it.unimi.dsi.fastutil.objects.Object2DoubleArrayMap; -@@ -832,7 +834,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - - protected AABB makeBoundingBox(Vec3 position) { -- return this.dimensions.makeBoundingBox(position); -+ if (this.lastDimensions == null || this.lastDimensions.width() != this.dimensions.width() || this.lastDimensions.height() != this.dimensions.height()) { -+ this.lastDimensions = this.dimensions; -+ this.cachedBoundingBox = this.dimensions.makeBoundingBox(Vec3.ZERO); -+ } -+ -+ return this.cachedBoundingBox.move(position); - } - - protected void reapplyPosition() { -@@ -1351,33 +1358,56 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - - public void applyEffectsFromBlocks(Vec3 oldPosition, Vec3 position) { -- if (this.isAffectedByBlocks()) { -- if (this.onGround()) { -- BlockPos onPosLegacy = this.getOnPosLegacy(); -+ if (!this.isAffectedByBlocks()) { -+ return; -+ } -+ -+ if (this.onGround()) { -+ BlockPos onPosLegacy = this.getOnPosLegacy(); -+ if (this.cachedSupportingBlockState == null || !this.isSupportedBy(onPosLegacy)) { - BlockState blockState = this.level().getBlockState(onPosLegacy); - blockState.getBlock().stepOn(this.level(), onPosLegacy, blockState, this); -+ this.cachedSupportingBlockState = blockState; -+ } else { -+ this.cachedSupportingBlockState.getBlock().stepOn(this.level(), onPosLegacy, this.cachedSupportingBlockState, this); - } -+ } - -+ if (oldPosition.distanceToSqr(position) > 1.0E-8) { - this.movementThisTick.add(new Entity.Movement(oldPosition, position)); -- List list = List.copyOf(this.movementThisTick); -- this.movementThisTick.clear(); -- this.checkInsideBlocks(list, this.blocksInside); -- boolean flag = Iterables.any(this.blocksInside, state -> state.is(BlockTags.FIRE) || state.is(Blocks.LAVA)); -- this.blocksInside.clear(); -- if (!flag && this.isAlive()) { -- if (this.remainingFireTicks <= 0) { -- this.setRemainingFireTicks(-this.getFireImmuneTicks()); -- } -+ } - -- if (this.wasOnFire && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { -- this.playEntityOnFireExtinguishedSound(); -- } -+ if (this.movementThisTick.isEmpty()) { -+ return; -+ } -+ -+ this.checkInsideBlocks(this.movementThisTick, this.blocksInside); -+ -+ boolean inFireOrLava = false; -+ for (BlockState state : this.blocksInside) { -+ if (state.is(BlockTags.FIRE) || state.is(Blocks.LAVA)) { -+ inFireOrLava = true; -+ break; - } -+ } -+ -+ this.movementThisTick.clear(); - -- if (this.isOnFire() && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { -+ if (!inFireOrLava && this.isAlive()) { -+ if (this.remainingFireTicks <= 0) { - this.setRemainingFireTicks(-this.getFireImmuneTicks()); - } -+ -+ if (this.wasOnFire && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { -+ this.playEntityOnFireExtinguishedSound(); -+ } - } -+ -+ if (this.isOnFire() && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { -+ this.setRemainingFireTicks(-this.getFireImmuneTicks()); -+ } -+ -+ this.blocksInside.clear(); - } - - public boolean isAffectedByBlocks() { -@@ -1706,50 +1736,109 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - public void recordMovementThroughBlocks(Vec3 oldPosition, Vec3 position) { - this.movementThisTick.add(new Entity.Movement(oldPosition, position)); - } -+ private final Int2ObjectMap blockStateCache = new Int2ObjectOpenHashMap<>(16); -+ private BlockPos lastBlockPos = BlockPos.ZERO; -+ private BlockState lastBlockState = null; - - private void checkInsideBlocks(List movements, Set blocksInside) { -- if (this.isAffectedByBlocks()) { -- LongSet set = this.visitedBlocks; -+ if (!this.isAffectedByBlocks()) { -+ return; -+ } -+ -+ this.visitedBlocks.clear(); -+ this.blockStateCache.clear(); -+ -+ for (Entity.Movement movement : movements) { -+ Vec3 fromPos = movement.from(); -+ Vec3 toPos = movement.to(); - -- for (Entity.Movement movement : movements) { -- Vec3 vec3 = movement.from(); -- Vec3 vec31 = movement.to(); -- AABB aabb = this.makeBoundingBox(vec31).deflate(1.0E-5F); -+ if (fromPos.distanceToSqr(toPos) < 1.0E-8) { -+ continue; -+ } -+ -+ AABB aabb = this.makeBoundingBox(toPos).deflate(1.0E-5F); -+ -+ int minX = Mth.floor(Math.min(fromPos.x, toPos.x) - aabb.getXsize()/2); -+ int maxX = Mth.ceil(Math.max(fromPos.x, toPos.x) + aabb.getXsize()/2); -+ int minY = Mth.floor(Math.min(fromPos.y, toPos.y) - aabb.getYsize()/2); -+ int maxY = Mth.ceil(Math.max(fromPos.y, toPos.y) + aabb.getYsize()/2); -+ int minZ = Mth.floor(Math.min(fromPos.z, toPos.z) - aabb.getZsize()/2); -+ int maxZ = Mth.ceil(Math.max(fromPos.z, toPos.z) + aabb.getZsize()/2); -+ -+ int minChunkX = minX >> 4; -+ int maxChunkX = maxX >> 4; -+ int minChunkZ = minZ >> 4; -+ int maxChunkZ = maxZ >> 4; - -- for (BlockPos blockPos : BlockGetter.boxTraverseBlocks(vec3, vec31, aabb)) { -- if (!this.isAlive()) { -- return; -+ BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); -+ -+ for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) { -+ for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ++) { -+ if (!level().hasChunkAt(chunkX, chunkZ)) { -+ continue; - } - -- BlockState blockState = this.level().getBlockState(blockPos); -- if (!blockState.isAir() && set.add(blockPos.asLong())) { -- try { -- VoxelShape entityInsideCollisionShape = blockState.getEntityInsideCollisionShape(this.level(), blockPos); -- if (entityInsideCollisionShape != Shapes.block() -- && !this.collidedWithShapeMovingFrom(vec3, vec31, blockPos, entityInsideCollisionShape)) { -- continue; -- } -+ int xStart = Math.max(minX, chunkX << 4); -+ int xEnd = Math.min(maxX, (chunkX << 4) + 15); -+ int zStart = Math.max(minZ, chunkZ << 4); -+ int zEnd = Math.min(maxZ, (chunkZ << 4) + 15); - -- blockState.entityInside(this.level(), blockPos, this); -- this.onInsideBlock(blockState); -- } catch (Throwable var16) { -- CrashReport crashReport = CrashReport.forThrowable(var16, "Colliding entity with block"); -- CrashReportCategory crashReportCategory = crashReport.addCategory("Block being collided with"); -- CrashReportCategory.populateBlockDetails(crashReportCategory, this.level(), blockPos, blockState); -- CrashReportCategory crashReportCategory1 = crashReport.addCategory("Entity being checked for collision"); -- this.fillCrashReportCategory(crashReportCategory1); -- throw new ReportedException(crashReport); -- } -+ for (int x = xStart; x <= xEnd; x++) { -+ for (int z = zStart; z <= zEnd; z++) { -+ for (int y = minY; y <= maxY; y++) { -+ if (!this.isAlive()) { -+ return; -+ } -+ -+ mutablePos.set(x, y, z); -+ long posLong = mutablePos.asLong(); -+ -+ if (!this.visitedBlocks.add(posLong)) { -+ continue; -+ } - -- blocksInside.add(blockState); -+ BlockState blockState; -+ if (mutablePos.equals(this.lastBlockPos)) { -+ blockState = this.lastBlockState; -+ } else { -+ blockState = this.level().getBlockState(mutablePos); -+ this.lastBlockPos = mutablePos.immutable(); -+ this.lastBlockState = blockState; -+ } -+ -+ if (blockState.isAir()) { -+ continue; -+ } -+ -+ try { -+ VoxelShape entityInsideCollisionShape = blockState.getEntityInsideCollisionShape(this.level(), mutablePos); -+ if (entityInsideCollisionShape != Shapes.block() && -+ !this.collidedWithShapeMovingFrom(fromPos, toPos, mutablePos, entityInsideCollisionShape)) { -+ continue; -+ } -+ -+ blockState.entityInside(this.level(), mutablePos, this); -+ this.onInsideBlock(blockState); -+ blocksInside.add(blockState); -+ } catch (Throwable var16) { -+ CrashReport crashReport = CrashReport.forThrowable(var16, "Colliding entity with block"); -+ CrashReportCategory crashReportCategory = crashReport.addCategory("Block being collided with"); -+ CrashReportCategory.populateBlockDetails(crashReportCategory, this.level(), mutablePos, blockState); -+ CrashReportCategory crashReportCategory1 = crashReport.addCategory("Entity being checked for collision"); -+ this.fillCrashReportCategory(crashReportCategory1); -+ throw new ReportedException(crashReport); -+ } -+ } -+ } - } - } - } -- -- set.clear(); - } - } - -+ private EntityDimensions lastDimensions = null; -+ private AABB cachedBoundingBox = null; -+ - private boolean collidedWithShapeMovingFrom(Vec3 oldPosition, Vec3 position, BlockPos pos, VoxelShape shape) { - AABB aabb = this.makeBoundingBox(oldPosition); - Vec3 vec3 = position.subtract(oldPosition); From b0bfeb2b1188ebaa830f1a56785df8e694bb7caf Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Fri, 11 Apr 2025 17:55:54 +0200 Subject: [PATCH 12/27] fix buffer resize --- ...-writeLongArray-during-chunk-loading.patch | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0127-Bulk-writes-to-writeLongArray-during-chunk-loading.patch b/leaf-server/minecraft-patches/features/0127-Bulk-writes-to-writeLongArray-during-chunk-loading.patch index 947081e1..8ac54e00 100644 --- a/leaf-server/minecraft-patches/features/0127-Bulk-writes-to-writeLongArray-during-chunk-loading.patch +++ b/leaf-server/minecraft-patches/features/0127-Bulk-writes-to-writeLongArray-during-chunk-loading.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Bulk writes to writeLongArray during chunk loading diff --git a/net/minecraft/network/FriendlyByteBuf.java b/net/minecraft/network/FriendlyByteBuf.java -index abb0141426fd716e79a947b9498a8351daa342fc..5353b8331cf9c428ab7ffba6ebeac4fa323af456 100644 +index abb0141426fd716e79a947b9498a8351daa342fc..6971f93c3f6008f2c2f99fc52e2a3058fd8b7659 100644 --- a/net/minecraft/network/FriendlyByteBuf.java +++ b/net/minecraft/network/FriendlyByteBuf.java -@@ -341,10 +341,33 @@ public class FriendlyByteBuf extends ByteBuf { +@@ -341,10 +341,43 @@ public class FriendlyByteBuf extends ByteBuf { public FriendlyByteBuf writeLongArray(long[] array) { this.writeVarInt(array.length); @@ -16,29 +16,39 @@ index abb0141426fd716e79a947b9498a8351daa342fc..5353b8331cf9c428ab7ffba6ebeac4fa + if (array.length == 0) { + return this; + } ++ int neededBytes = array.length * Long.BYTES; ++ int maxWritableBytes = this.source.maxWritableBytes(); + -+ this.source.ensureWritable(array.length * Long.BYTES); -+ int writerIndex = this.source.writerIndex(); ++ if (maxWritableBytes >= neededBytes) { ++ this.source.ensureWritable(neededBytes); ++ int writerIndex = this.source.writerIndex(); + -+ if (this.source.hasArray()) { -+ byte[] dest = this.source.array(); -+ int offset = this.source.arrayOffset() + writerIndex; ++ if (this.source.hasArray()) { ++ byte[] dest = this.source.array(); ++ int offset = this.source.arrayOffset() + writerIndex; ++ ++ ByteBuffer buf = ByteBuffer.wrap(dest, offset, neededBytes).order(this.source.order()); ++ buf.asLongBuffer().put(array); - for (long l : array) { - this.writeLong(l); -+ ByteBuffer buf = ByteBuffer.wrap(dest, offset, array.length * Long.BYTES).order(this.source.order()); -+ buf.asLongBuffer().put(array); -+ -+ this.source.writerIndex(writerIndex + array.length * Long.BYTES); -+ } else if (this.source.nioBufferCount() > 0) { -+ ByteBuffer nioBuf = this.source.nioBuffer(writerIndex, array.length * Long.BYTES); -+ nioBuf.asLongBuffer().put(array); -+ this.source.writerIndex(writerIndex + array.length * Long.BYTES); ++ this.source.writerIndex(writerIndex + neededBytes); ++ } else if (this.source.nioBufferCount() > 0) { ++ ByteBuffer nioBuf = this.source.nioBuffer(writerIndex, neededBytes); ++ nioBuf.asLongBuffer().put(array); ++ this.source.writerIndex(writerIndex + neededBytes); ++ } else { ++ ByteBuffer temp = ByteBuffer.allocate(neededBytes).order(this.source.order()); ++ temp.asLongBuffer().put(array); ++ temp.rewind(); ++ this.source.writeBytes(temp); ++ } + } else { -+ ByteBuffer temp = ByteBuffer.allocate(array.length * Long.BYTES).order(this.source.order()); -+ temp.asLongBuffer().put(array); -+ temp.rewind(); -+ this.source.writeBytes(temp); ++ // Not enough space even at max capacity, use traditional approach ++ // which will write each element individually (and handle growing the buffer as needed) ++ for (long l : array) { ++ this.writeLong(l); ++ } } + // Leaf end - Bulk writes to writeLongArray during chunk loading From 4c9fb21bb9b851e54d1d430c2ba69e7fb62a44d5 Mon Sep 17 00:00:00 2001 From: Dreeam-qwq <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sat, 12 Apr 2025 01:40:03 +0000 Subject: [PATCH 13/27] Updated Upstream (Gale) Upstream has released updates that appear to apply and compile correctly Gale Changes: Dreeam-qwq/Gale@4ec6e8a5 Updated Upstream (Paper) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 93e7b98e..3418093f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ group=cn.dreeam.leaf mcVersion=1.21.4 version=1.21.4-R0.1-SNAPSHOT -galeCommit=966b5c6859a109c0cf28b09527748dc68a79e37f +galeCommit=4ec6e8a5964d1ebdfc9b118c9564a506605aa6bf org.gradle.configuration-cache=true org.gradle.caching=true From caf961ac088d97732b8e29a4ec8744fbb1cdf9c9 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sat, 12 Apr 2025 16:47:06 -0400 Subject: [PATCH 14/27] Updated Upstream (Purpur) Upstream has released updates that appear to apply and compile correctly Purpur Changes: PurpurMC/Purpur@135e2c41 Updated Upstream (Paper) PurpurMC/Purpur@e19b6707 Updated Upstream (Paper) PurpurMC/Purpur@304174ec Updated Upstream (Paper) PurpurMC/Purpur@4a3b139f Updated Upstream (Paper) PurpurMC/Purpur@a14011f4 Updated Upstream (Paper) PurpurMC/Purpur@3a1b29a9 [ci/skip] Updated Upstream (Paper) PurpurMC/Purpur@5d1df704 Updated Upstream (Paper) PurpurMC/Purpur@bc8c597b Updated Upstream (Paper) PurpurMC/Purpur@ca56b414 Updated Upstream (Paper) PurpurMC/Purpur@75efb975 Updated Upstream (Paper) PurpurMC/Purpur@9249a2ca Updated Upstream (Paper) PurpurMC/Purpur@8c293626 [ci/skip] ignore deprecation or removal warnings PurpurMC/Purpur@65b1288b Updated Upstream (Paper) PurpurMC/Purpur@96f0ee1e Add check for max growth age special case (#1652) --- .../paper-patches/features/0004-Purpur-API-Changes.patch | 2 +- .../features/0007-Purpur-Server-Minecraft-Changes.patch | 6 +++--- .../features/0005-Purpur-Server-Paper-Changes.patch | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/leaf-api/paper-patches/features/0004-Purpur-API-Changes.patch b/leaf-api/paper-patches/features/0004-Purpur-API-Changes.patch index 0aca8929..85237c48 100644 --- a/leaf-api/paper-patches/features/0004-Purpur-API-Changes.patch +++ b/leaf-api/paper-patches/features/0004-Purpur-API-Changes.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Purpur API Changes Original license: MIT Original project: https://github.com/PurpurMC/Purpur -Commit: b34d675fef91bae2df723705f2568c7afd552d2d +Commit: 96f0ee1ea36729daa5cf6265c9cbae2849bcfd3b Patches listed below are removed in this patch, They exists in Gale or Leaf: * "co/aikar/timings/TimedEventExecutor.java.patch" diff --git a/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch b/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch index de6d525b..9a85a58e 100644 --- a/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch +++ b/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Purpur Server Minecraft Changes Original license: MIT Original project: https://github.com/PurpurMC/Purpur -Commit: b34d675fef91bae2df723705f2568c7afd552d2d +Commit: 96f0ee1ea36729daa5cf6265c9cbae2849bcfd3b Patches listed below are removed in this patch, They exists in Gale or Leaf: * "net/minecraft/CrashReport.java.patch" @@ -15451,7 +15451,7 @@ index 1fdede769b67cb5d2f9159c779f19e3639bb6ff5..2ff5457300d66378dbbea492deff0136 } diff --git a/net/minecraft/world/level/block/GrowingPlantHeadBlock.java b/net/minecraft/world/level/block/GrowingPlantHeadBlock.java -index 0994f7265322d1f33365a1df0faaffd9df05fcc0..5dc1883ecd6e52666f553f30c150843c99fbef08 100644 +index 0994f7265322d1f33365a1df0faaffd9df05fcc0..b81c7cabb4c9f81de6d4c6b8ffde70e8a07d8fd1 100644 --- a/net/minecraft/world/level/block/GrowingPlantHeadBlock.java +++ b/net/minecraft/world/level/block/GrowingPlantHeadBlock.java @@ -34,12 +34,12 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements @@ -15459,7 +15459,7 @@ index 0994f7265322d1f33365a1df0faaffd9df05fcc0..5dc1883ecd6e52666f553f30c150843c @Override public BlockState getStateForPlacement(RandomSource random) { - return this.defaultBlockState().setValue(AGE, Integer.valueOf(random.nextInt(25))); -+ return this.defaultBlockState().setValue(AGE, Integer.valueOf(random.nextInt(getMaxGrowthAge()))); // Purpur - kelp, cave, weeping, and twisting configurable max growth age ++ return this.defaultBlockState().setValue(AGE, Integer.valueOf(getMaxGrowthAge() == 0 ? 0 : random.nextInt(getMaxGrowthAge()))); // Purpur - kelp, cave, weeping, and twisting configurable max growth age } @Override diff --git a/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch b/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch index df829a25..26010e8a 100644 --- a/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch +++ b/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Purpur Server Paper Changes Original license: MIT Original project: https://github.com/PurpurMC/Purpur -Commit: b34d675fef91bae2df723705f2568c7afd552d2d +Commit: 96f0ee1ea36729daa5cf6265c9cbae2849bcfd3b Patches listed below are removed in this patch, They exists in Gale or Leaf: * "Rebrand.patch" From bb67247bbd42834581e46a842945366032acb27d Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 13 Apr 2025 02:17:01 +0200 Subject: [PATCH 15/27] fix bfs on getSlopeDistance --- .../0144-Use-BFS-on-getSlopeDistance.patch | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0144-Use-BFS-on-getSlopeDistance.patch b/leaf-server/minecraft-patches/features/0144-Use-BFS-on-getSlopeDistance.patch index 5b3603bb..3532adf9 100644 --- a/leaf-server/minecraft-patches/features/0144-Use-BFS-on-getSlopeDistance.patch +++ b/leaf-server/minecraft-patches/features/0144-Use-BFS-on-getSlopeDistance.patch @@ -9,7 +9,7 @@ Leaf: ~48ms (-36%) This should help drastically on the farms that use actively changing fluids. diff --git a/net/minecraft/world/level/material/FlowingFluid.java b/net/minecraft/world/level/material/FlowingFluid.java -index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..618a8af8fe30c39bb8c93dcc324f4ae1c48e704e 100644 +index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..33e5c19362de8b4002c23959661535b835eb0ce5 100644 --- a/net/minecraft/world/level/material/FlowingFluid.java +++ b/net/minecraft/world/level/material/FlowingFluid.java @@ -8,6 +8,8 @@ import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; @@ -21,15 +21,15 @@ index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..618a8af8fe30c39bb8c93dcc324f4ae1 import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; -@@ -341,31 +343,72 @@ public abstract class FlowingFluid extends Fluid { +@@ -341,31 +343,76 @@ public abstract class FlowingFluid extends Fluid { protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state, BlockPos source) { beforeDestroyingBlock(level, pos, state); } // Paper - Add BlockBreakBlockEvent protected abstract void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state); - protected int getSlopeDistance(LevelReader level, BlockPos pos, int depth, Direction direction, BlockState state, FlowingFluid.SpreadContext spreadContext) { - int i = 1000; + protected int getSlopeDistance(LevelReader level, BlockPos startPos, int initialDepth, Direction excludedDirection, BlockState startState, FlowingFluid.SpreadContext spreadContext) { -+ it.unimi.dsi.fastutil.longs.LongSet visited = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(512); // Pre-allocate capacity -+ java.util.Queue queue = new java.util.ArrayDeque<>(64); // Optimized initial capacity ++ it.unimi.dsi.fastutil.longs.LongSet visited = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(512); ++ java.util.Queue queue = new java.util.ArrayDeque<>(256); - for (Direction direction1 : Direction.Plane.HORIZONTAL) { - if (direction1 != direction) { @@ -54,9 +54,14 @@ index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..618a8af8fe30c39bb8c93dcc324f4ae1 + BlockState neighborState = spreadContext.getBlockStateIfLoaded(neighborPos); + if (neighborState == null) continue; + ++ // Check if the fluid can actually pass through to this first neighbor before adding ++ FluidState neighborFluidState = neighborState.getFluidState(); ++ if (!this.canPassThrough(level, this.getFlowing(), startPos, startState, dir, neighborPos, neighborState, neighborFluidState)) { ++ continue; ++ } + long visitKey = encodeSlopeNode(neighborPos, dir.getOpposite()); + if (visited.add(visitKey)) { -+ queue.add(new FlowingFluid.SlopeDistanceNode(neighborPos, initialDepth + 1, dir.getOpposite(), neighborState)); ++ queue.add(new FlowingFluid.SlopeDistanceNode(neighborPos, initialDepth, dir.getOpposite(), neighborState)); + } + } + @@ -66,12 +71,11 @@ index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..618a8af8fe30c39bb8c93dcc324f4ae1 + // Process the queue + while (!queue.isEmpty()) { + FlowingFluid.SlopeDistanceNode current = queue.poll(); -+ if (current.depth >= slopeFindDistance) continue; -+ + if (spreadContext.isHole(current.pos)) { + return current.depth; + } + ++ if (current.depth >= slopeFindDistance) continue; + for (Direction dir : Direction.Plane.HORIZONTAL) { + if (dir == current.excludedDir) continue; + @@ -92,7 +96,7 @@ index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..618a8af8fe30c39bb8c93dcc324f4ae1 } - return i; -+ return minDistance; // Return fallback value ++ return minDistance; + } + + private static long encodeSlopeNode(BlockPos pos, Direction excludedDir) { From 2308f08ae30e8bcf83c4ed69bf7b830cfcc9cd5e Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 13 Apr 2025 03:23:40 +0200 Subject: [PATCH 16/27] make StringCanonizingOpenHashMap faster --- .../util/map/StringCanonizingOpenHashMap.java | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/StringCanonizingOpenHashMap.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/StringCanonizingOpenHashMap.java index e55c07c9..45f1336c 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/map/StringCanonizingOpenHashMap.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/StringCanonizingOpenHashMap.java @@ -11,21 +11,31 @@ import java.util.function.Function; public class StringCanonizingOpenHashMap extends Object2ObjectOpenHashMap { private static final Interner KEY_INTERNER = Interners.newWeakInterner(); + private final float loadFactor; private static String intern(String key) { - return key != null ? KEY_INTERNER.intern(key) : null; + if (key == null) return null; + String jvmInterned = key.intern(); + if (jvmInterned == key) { + return key; + } + + return KEY_INTERNER.intern(key); } public StringCanonizingOpenHashMap() { super(); + this.loadFactor = 0.8f; } public StringCanonizingOpenHashMap(int expectedSize) { super(expectedSize); + this.loadFactor = 0.8f; } public StringCanonizingOpenHashMap(int expectedSize, float loadFactor) { super(expectedSize, loadFactor); + this.loadFactor = loadFactor; } @Override @@ -36,9 +46,16 @@ public class StringCanonizingOpenHashMap extends Object2ObjectOpenHashMap m) { if (m.isEmpty()) return; - Map tmp = new Object2ObjectOpenHashMap<>(m.size()); - m.forEach((k, v) -> tmp.put(intern(k), v)); - super.putAll(tmp); + + // Fast path for maps that already have interned keys + if (m instanceof StringCanonizingOpenHashMap) { + super.putAll(m); + return; + } + // Process each entry directly rather than creating a temporary map + for (Map.Entry entry : m.entrySet()) { + put(entry.getKey(), entry.getValue()); + } } private void putWithoutInterning(String key, T value) { @@ -46,11 +63,17 @@ public class StringCanonizingOpenHashMap extends Object2ObjectOpenHashMap StringCanonizingOpenHashMap deepCopy(StringCanonizingOpenHashMap incomingMap, Function deepCopier) { - StringCanonizingOpenHashMap newMap = new StringCanonizingOpenHashMap<>(incomingMap.size(), 0.8f); + int size = incomingMap.size(); + if (size == 0) { + return new StringCanonizingOpenHashMap<>(0, incomingMap.loadFactor); + } + // Pre-allocate + StringCanonizingOpenHashMap newMap = new StringCanonizingOpenHashMap<>(size, incomingMap.loadFactor); ObjectIterator> iterator = incomingMap.object2ObjectEntrySet().fastIterator(); while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); + Entry entry = iterator.next(); + // Keys are already interned, so we can add them directly newMap.putWithoutInterning(entry.getKey(), deepCopier.apply(entry.getValue())); } From 3d7b15ba53837fffbd64e84ae9bb026f936415d7 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 13 Apr 2025 03:59:07 +0200 Subject: [PATCH 17/27] cleanup and make concurrency level higher --- .../util/map/StringCanonizingOpenHashMap.java | 34 ++++--------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/StringCanonizingOpenHashMap.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/StringCanonizingOpenHashMap.java index 45f1336c..d909eddf 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/map/StringCanonizingOpenHashMap.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/StringCanonizingOpenHashMap.java @@ -10,32 +10,22 @@ import java.util.function.Function; public class StringCanonizingOpenHashMap extends Object2ObjectOpenHashMap { - private static final Interner KEY_INTERNER = Interners.newWeakInterner(); - private final float loadFactor; + private static final Interner KEY_INTERNER = Interners.newBuilder().weak().concurrencyLevel(16).build(); private static String intern(String key) { - if (key == null) return null; - String jvmInterned = key.intern(); - if (jvmInterned == key) { - return key; - } - - return KEY_INTERNER.intern(key); + return key != null ? KEY_INTERNER.intern(key) : null; } public StringCanonizingOpenHashMap() { super(); - this.loadFactor = 0.8f; } public StringCanonizingOpenHashMap(int expectedSize) { super(expectedSize); - this.loadFactor = 0.8f; } public StringCanonizingOpenHashMap(int expectedSize, float loadFactor) { super(expectedSize, loadFactor); - this.loadFactor = loadFactor; } @Override @@ -46,15 +36,9 @@ public class StringCanonizingOpenHashMap extends Object2ObjectOpenHashMap m) { if (m.isEmpty()) return; - - // Fast path for maps that already have interned keys - if (m instanceof StringCanonizingOpenHashMap) { - super.putAll(m); - return; - } - // Process each entry directly rather than creating a temporary map + ensureCapacity(size() + m.size()); for (Map.Entry entry : m.entrySet()) { - put(entry.getKey(), entry.getValue()); + super.put(intern(entry.getKey()), entry.getValue()); } } @@ -63,17 +47,11 @@ public class StringCanonizingOpenHashMap extends Object2ObjectOpenHashMap StringCanonizingOpenHashMap deepCopy(StringCanonizingOpenHashMap incomingMap, Function deepCopier) { - int size = incomingMap.size(); - if (size == 0) { - return new StringCanonizingOpenHashMap<>(0, incomingMap.loadFactor); - } - // Pre-allocate - StringCanonizingOpenHashMap newMap = new StringCanonizingOpenHashMap<>(size, incomingMap.loadFactor); + StringCanonizingOpenHashMap newMap = new StringCanonizingOpenHashMap<>(incomingMap.size(), incomingMap.f); ObjectIterator> iterator = incomingMap.object2ObjectEntrySet().fastIterator(); while (iterator.hasNext()) { - Entry entry = iterator.next(); - // Keys are already interned, so we can add them directly + Map.Entry entry = iterator.next(); newMap.putWithoutInterning(entry.getKey(), deepCopier.apply(entry.getValue())); } From f922d959cc69cef58a7345ec818318052dc3b63f Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sun, 13 Apr 2025 02:06:23 -0400 Subject: [PATCH 18/27] Update prepareRelease base script * Cleanup * Extract jar name & current tag as var for easy manage * Only delete release tag if exists, to fix build failed --- scripts/prepareRelease.sh | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/scripts/prepareRelease.sh b/scripts/prepareRelease.sh index 9edd80ae..c38cf43c 100755 --- a/scripts/prepareRelease.sh +++ b/scripts/prepareRelease.sh @@ -4,10 +4,12 @@ set -e IS_EOL=false IS_UNSUPPORTED=false +JAR_NAME="leaf-1.21.4" +CURRENT_TAG="ver-1.21.4" RELEASE_NOTES="release_notes.md" # Rename Leaf jar -mv ./leaf-1.21.4-"${BUILD_NUMBER}"-mojmap.jar ./leaf-1.21.4-"${BUILD_NUMBER}".jar +mv ./$JAR_NAME-${BUILD_NUMBER}-mojmap.jar ./$JAR_NAME-${BUILD_NUMBER}.jar # Branch name CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) @@ -15,7 +17,7 @@ echo "✨Current branch: $CURRENT_BRANCH" # Latest tag name LATEST_TAG=$(git describe --tags --abbrev=0) -if [ -z "$LATEST_TAG" ]; then +if [ -z $LATEST_TAG ]; then LATEST_TAG=$(git rev-list --max-parents=0 HEAD) echo "⚠️No previous release found. Using initial commit." else @@ -23,12 +25,12 @@ else fi # Commit of the latest tag -LAST_RELEASE_COMMIT=$(git rev-list -n 1 "$LATEST_TAG") +LAST_RELEASE_COMMIT=$(git rev-list -n 1 $LATEST_TAG) echo "✨Last release commit: $LAST_RELEASE_COMMIT" # Commits log -COMMIT_LOG=$(git log "$LAST_RELEASE_COMMIT"..HEAD --pretty=format:"- [\`%h\`](https://github.com/"${GITHUB_REPO}"/commit/%H) %s (%an)") -if [ -z "$COMMIT_LOG" ]; then +COMMIT_LOG=$(git log $LAST_RELEASE_COMMIT..HEAD --pretty=format:"- [\`%h\`](https://github.com/${GITHUB_REPO}/commit/%H) %s (%an)") +if [ -z $COMMIT_LOG ]; then COMMIT_LOG="⚠️No new commits since $LATEST_TAG." else echo "✅Commits log generated" @@ -47,11 +49,11 @@ echo "" >> $RELEASE_NOTES } >> $RELEASE_NOTES # Get checksums -file="./leaf-1.21.4-"${BUILD_NUMBER}".jar" -if [ -f "$file" ]; then - MD5=$(md5sum "$file" | awk '{ print $1 }') - SHA256=$(sha256sum "$file" | awk '{ print $1 }') - FILENAME=$(basename "$file") +file="./$JAR_NAME-${BUILD_NUMBER}.jar" +if [ -f $file ]; then + MD5=$(md5sum $file | awk '{ print $1 }') + SHA256=$(sha256sum $file | awk '{ print $1 }') + FILENAME=$(basename $file) { echo "| | $FILENAME |" @@ -68,7 +70,7 @@ else fi # EOL warning -if [ "$IS_EOL" = true ]; then +if [ $IS_EOL = true ]; then { echo "" echo "> [!WARNING]" @@ -78,7 +80,7 @@ if [ "$IS_EOL" = true ]; then fi # Unsupported warning -if [ "$IS_UNSUPPORTED" = true ]; then +if [ $IS_UNSUPPORTED = true ]; then { echo "" echo "> [!CAUTION]" @@ -87,6 +89,10 @@ if [ "$IS_UNSUPPORTED" = true ]; then } >> $RELEASE_NOTES fi -# Delete last tag -gh release delete ver-1.21.4 --cleanup-tag -y -R "${GITHUB_REPO}" +# Delete current release tag +if git show-ref --tags $CURRENT_TAG --quiet; then + { + git push --delete origin $CURRENT_TAG + } +fi echo "🚀Ready for release" From d5d6ac4a4485fa6860253f9cba16b941aa10b9f5 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sun, 13 Apr 2025 19:43:38 -0400 Subject: [PATCH 19/27] Updated Upstream (Purpur) Upstream has released updates that appear to apply and compile correctly Purpur Changes: PurpurMC/Purpur@d4af7947 Add configurable smooth snow accumulation (#1651) PurpurMC/Purpur@51aafbc7 Final 1.21.4 Upstream (Paper) --- .../features/0004-Purpur-API-Changes.patch | 2 +- ...0007-Purpur-Server-Minecraft-Changes.patch | 79 +++++++++++++++---- .../features/0025-Leaves-Replay-Mod-API.patch | 12 +-- ...missing-purpur-configuration-options.patch | 4 +- .../features/0085-Multithreaded-Tracker.patch | 6 +- .../features/0105-Cache-chunk-key.patch | 4 +- ...-SparklyPaper-Parallel-world-ticking.patch | 12 +-- .../0005-Purpur-Server-Paper-Changes.patch | 2 +- 8 files changed, 85 insertions(+), 36 deletions(-) diff --git a/leaf-api/paper-patches/features/0004-Purpur-API-Changes.patch b/leaf-api/paper-patches/features/0004-Purpur-API-Changes.patch index 85237c48..13bbe446 100644 --- a/leaf-api/paper-patches/features/0004-Purpur-API-Changes.patch +++ b/leaf-api/paper-patches/features/0004-Purpur-API-Changes.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Purpur API Changes Original license: MIT Original project: https://github.com/PurpurMC/Purpur -Commit: 96f0ee1ea36729daa5cf6265c9cbae2849bcfd3b +Commit: 51aafbc731b33dfebc4b6180970570cfbbd14d3c Patches listed below are removed in this patch, They exists in Gale or Leaf: * "co/aikar/timings/TimedEventExecutor.java.patch" diff --git a/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch b/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch index 9a85a58e..43a468d1 100644 --- a/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch +++ b/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Purpur Server Minecraft Changes Original license: MIT Original project: https://github.com/PurpurMC/Purpur -Commit: 96f0ee1ea36729daa5cf6265c9cbae2849bcfd3b +Commit: 51aafbc731b33dfebc4b6180970570cfbbd14d3c Patches listed below are removed in this patch, They exists in Gale or Leaf: * "net/minecraft/CrashReport.java.patch" @@ -724,7 +724,7 @@ index f262a7c5ae4e7d56f16f5c0f4f145a2e428abbe4..614c7d9f673c926562acc8fa3b378862 private JComponent buildOnboardingPanel() { String onboardingLink = "https://docs.papermc.io/paper/next-steps"; diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 101f1a87a5fe920b57a5179da41cc91d88afa32e..94ee31a4a02edb003b98a09b0311355c1db4f547 100644 +index 101f1a87a5fe920b57a5179da41cc91d88afa32e..b49dd636e730f0c5b609df68ee51bcd12efc1eaa 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -205,6 +205,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -801,29 +801,28 @@ index 101f1a87a5fe920b57a5179da41cc91d88afa32e..94ee31a4a02edb003b98a09b0311355c this.setDayTime(this.levelData.getDayTime() + 1L); } } -@@ -840,8 +868,22 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -840,7 +868,21 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe public void setDayTime(long time) { this.serverLevelData.setDayTime(time); + // Purpur start - Configurable daylight cycle + this.preciseTime = time; + this.forceTime = false; -+ } + } + public void setDayTime(double i) { + this.serverLevelData.setDayTime((long) i); + this.forceTime = true; + // Purpur end - Configurable daylight cycle - } - ++ } ++ + // Purpur start - Configurable daylight cycle + public boolean isForceTime() { + return this.forceTime; + } + // Purpur end - Configurable daylight cycle -+ + public void tickCustomSpawners(boolean spawnEnemies, boolean spawnFriendlies) { for (CustomSpawner customSpawner : this.customSpawners) { - customSpawner.tick(this, spawnEnemies, spawnFriendlies); @@ -919,9 +961,17 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe && this.random.nextDouble() < currentDifficultyAt.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01) // Paper - Configurable spawn chances for skeleton horses && !this.getBlockState(blockPos.below()).is(Blocks.LIGHTNING_ROD); @@ -844,7 +843,46 @@ index 101f1a87a5fe920b57a5179da41cc91d88afa32e..94ee31a4a02edb003b98a09b0311355c skeletonHorse.setAge(0); skeletonHorse.setPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()); this.addFreshEntity(skeletonHorse, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit -@@ -989,7 +1039,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -966,9 +1016,35 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + if (blockState.is(Blocks.SNOW)) { + int layersValue = blockState.getValue(SnowLayerBlock.LAYERS); + if (layersValue < Math.min(_int, 8)) { +- BlockState blockState1 = blockState.setValue(SnowLayerBlock.LAYERS, Integer.valueOf(layersValue + 1)); +- Block.pushEntitiesUp(blockState, blockState1, this, heightmapPos); +- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, heightmapPos, blockState1, null); // CraftBukkit ++ // Purpur start - Smooth snow accumulation ++ boolean canSnow = true; ++ // Ensure snow doesn't get more than N layers taller than its neighbors ++ // We only need to check blocks that are taller than the minimum step height ++ if (layersValue >= org.purpurmc.purpur.PurpurConfig.smoothSnowAccumulationStep && org.purpurmc.purpur.PurpurConfig.smoothSnowAccumulationStep > 0) { ++ int layersValueMin = layersValue - org.purpurmc.purpur.PurpurConfig.smoothSnowAccumulationStep; ++ for (Direction direction : Direction.Plane.HORIZONTAL) { ++ BlockPos blockPosNeighbor = heightmapPos.relative(direction); ++ BlockState blockStateNeighbor = this.getBlockState(blockPosNeighbor); ++ if (blockStateNeighbor.is(Blocks.SNOW)) { ++ // Special check for snow layers, if neighbors are too short, don't accumulate ++ int layersValueNeighbor = blockStateNeighbor.getValue(SnowLayerBlock.LAYERS); ++ if (layersValueNeighbor <= layersValueMin) { ++ canSnow = false; ++ break; ++ } ++ } else if (!Block.isFaceFull(blockStateNeighbor.getCollisionShape(this, blockPosNeighbor), direction.getOpposite())) { ++ // Since our layer is tall enough already, if we have a non-full neighbor block, don't accumulate ++ canSnow = false; ++ break; ++ } ++ } ++ } ++ if (canSnow) { ++ BlockState blockState1 = blockState.setValue(SnowLayerBlock.LAYERS, Integer.valueOf(layersValue + 1)); ++ Block.pushEntitiesUp(blockState, blockState1, this, heightmapPos); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, heightmapPos, blockState1, null); // CraftBukkit ++ } ++ // Purpur end - Smooth snow accumulation + } + } else { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, heightmapPos, Blocks.SNOW.defaultBlockState(), null); // CraftBukkit +@@ -989,7 +1065,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe pointOfInterestType -> pointOfInterestType.is(PoiTypes.LIGHTNING_ROD), blockPos -> blockPos.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, blockPos.getX(), blockPos.getZ()) - 1, pos, @@ -853,7 +891,7 @@ index 101f1a87a5fe920b57a5179da41cc91d88afa32e..94ee31a4a02edb003b98a09b0311355c PoiManager.Occupancy.ANY ); return optional.map(blockPos -> blockPos.above(1)); -@@ -1037,8 +1087,26 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1037,8 +1113,26 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe int _int = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); Component component; if (this.sleepStatus.areEnoughSleeping(_int)) { @@ -880,7 +918,7 @@ index 101f1a87a5fe920b57a5179da41cc91d88afa32e..94ee31a4a02edb003b98a09b0311355c component = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(_int)); } -@@ -1171,6 +1239,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1171,6 +1265,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @VisibleForTesting public void resetWeatherCycle() { // CraftBukkit start @@ -888,7 +926,7 @@ index 101f1a87a5fe920b57a5179da41cc91d88afa32e..94ee31a4a02edb003b98a09b0311355c this.serverLevelData.setRaining(false, org.bukkit.event.weather.WeatherChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents // If we stop due to everyone sleeping we should reset the weather duration to some other random value. // Not that everyone ever manages to get the whole server to sleep at the same time.... -@@ -1178,6 +1247,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1178,6 +1273,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe this.serverLevelData.setRainTime(0); } // CraftBukkit end @@ -896,7 +934,7 @@ index 101f1a87a5fe920b57a5179da41cc91d88afa32e..94ee31a4a02edb003b98a09b0311355c this.serverLevelData.setThundering(false, org.bukkit.event.weather.ThunderChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents // CraftBukkit start // If we stop due to everyone sleeping we should reset the weather duration to some other random value. -@@ -2660,7 +2730,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -2660,7 +2756,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Spigot start if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message // Paper start - Fix merchant inventory not closing on entity removal @@ -16769,10 +16807,10 @@ index 85148858db1fd5e9da8bbdde4b0d84110d80e373..c9c6e4e460ad8435f12761704bb9b028 } diff --git a/org/purpurmc/purpur/PurpurConfig.java b/org/purpurmc/purpur/PurpurConfig.java new file mode 100644 -index 0000000000000000000000000000000000000000..bbfd05509dfc2ee453f847d299b3d261324b6fa9 +index 0000000000000000000000000000000000000000..1cc5c37ee9aab6b9eb45881dddc03715bd4153b7 --- /dev/null +++ b/org/purpurmc/purpur/PurpurConfig.java -@@ -0,0 +1,581 @@ +@@ -0,0 +1,592 @@ +package org.purpurmc.purpur; + +import com.google.common.base.Throwables; @@ -17102,6 +17140,7 @@ index 0000000000000000000000000000000000000000..bbfd05509dfc2ee453f847d299b3d261 + public static boolean cryingObsidianValidForPortalFrame = false; + public static int beeInsideBeeHive = 3; + public static boolean anvilCumulativeCost = true; ++ public static int smoothSnowAccumulationStep = 0; + public static int lightningRodRange = 128; + public static Set grindstoneIgnoredEnchants = new HashSet<>(); + public static boolean grindstoneRemoveAttributes = false; @@ -17145,6 +17184,16 @@ index 0000000000000000000000000000000000000000..bbfd05509dfc2ee453f847d299b3d261 + cryingObsidianValidForPortalFrame = getBoolean("settings.blocks.crying_obsidian.valid-for-portal-frame", cryingObsidianValidForPortalFrame); + beeInsideBeeHive = getInt("settings.blocks.beehive.max-bees-inside", beeInsideBeeHive); + anvilCumulativeCost = getBoolean("settings.blocks.anvil.cumulative-cost", anvilCumulativeCost); ++ smoothSnowAccumulationStep = getInt("settings.blocks.snow.smooth-accumulation-step", smoothSnowAccumulationStep); ++ if (smoothSnowAccumulationStep > 7) { ++ smoothSnowAccumulationStep = 7; ++ log(Level.WARNING, "blocks.snow.smooth-accumulation-step is set to above maximum allowed value of 7"); ++ log(Level.WARNING, "Using value of 7 to prevent issues"); ++ } else if (smoothSnowAccumulationStep < 0) { ++ smoothSnowAccumulationStep = 0; ++ log(Level.WARNING, "blocks.snow.smooth-accumulation-step is set to below minimum allowed value of 0"); ++ log(Level.WARNING, "Using value of 0 to prevent issues"); ++ } + lightningRodRange = getInt("settings.blocks.lightning_rod.range", lightningRodRange); + ArrayList defaultCurses = new ArrayList<>(){{ + add("minecraft:binding_curse"); diff --git a/leaf-server/minecraft-patches/features/0025-Leaves-Replay-Mod-API.patch b/leaf-server/minecraft-patches/features/0025-Leaves-Replay-Mod-API.patch index 811bd094..e1f45d70 100644 --- a/leaf-server/minecraft-patches/features/0025-Leaves-Replay-Mod-API.patch +++ b/leaf-server/minecraft-patches/features/0025-Leaves-Replay-Mod-API.patch @@ -105,7 +105,7 @@ index dddbb18992348fb7e8a6552423d134809cd7fdbc..0e6e71030e3fd1335fff796b861524a4 if (this.hidesOnlinePlayers()) { return new ServerStatus.Players(maxPlayers, players.size(), List.of()); diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java -index 792ba93b531e9586e26aafa00830022a8996fc04..e4ea26ae84efde7ce54e08a246a6ea2ae2a17151 100644 +index abccabb8a0a1a9730b7df070dd25f3ca215af362..0a4bcc4c44fed2ededafaf0641315e072b7ba771 100644 --- a/net/minecraft/server/PlayerAdvancements.java +++ b/net/minecraft/server/PlayerAdvancements.java @@ -168,6 +168,11 @@ public class PlayerAdvancements { @@ -134,7 +134,7 @@ index 5c0a04db38821dbb0cba2bb6f0787f113d167efd..cd153db93f709c3142942fac88ae3ca2 .filter(player -> !playerList.isOp(player.getGameProfile())) .map(player -> player.getGameProfile().getName()), diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 94ee31a4a02edb003b98a09b0311355c1db4f547..f8bd39ddd7b6948734254acfb8b0235eff774133 100644 +index b49dd636e730f0c5b609df68ee51bcd12efc1eaa..5e971bca365c692d4ce0c58693592002ce01471c 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -216,6 +216,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -153,7 +153,7 @@ index 94ee31a4a02edb003b98a09b0311355c1db4f547..f8bd39ddd7b6948734254acfb8b0235e } // Paper start -@@ -2672,6 +2674,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -2698,6 +2700,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server; moved down below valid=true if (entity instanceof ServerPlayer serverPlayer) { ServerLevel.this.players.add(serverPlayer); @@ -165,7 +165,7 @@ index 94ee31a4a02edb003b98a09b0311355c1db4f547..f8bd39ddd7b6948734254acfb8b0235e ServerLevel.this.updateSleepingPlayerList(); } -@@ -2742,6 +2749,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -2768,6 +2775,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe ServerLevel.this.getChunkSource().removeEntity(entity); if (entity instanceof ServerPlayer serverPlayer) { ServerLevel.this.players.remove(serverPlayer); @@ -178,7 +178,7 @@ index 94ee31a4a02edb003b98a09b0311355c1db4f547..f8bd39ddd7b6948734254acfb8b0235e } diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 622257dbbe572de33e15abef9055016268730261..dfb4524d80f642eff1b146dd2fbfa07f21d844c6 100644 +index d44c3baa2ef30d5cd4c46e491ff9198fa558513c..f89d28595fa9ca12e414f7b3cc86085ff0769e72 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -195,7 +195,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @@ -191,7 +191,7 @@ index 622257dbbe572de33e15abef9055016268730261..dfb4524d80f642eff1b146dd2fbfa07f private final ServerStatsCounter stats; private float lastRecordedHealthAndAbsorption = Float.MIN_VALUE; diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index e3d09d5f4efb32bb276e001e5ee747a775b502ee..78b15d750d75e5d4c2318a3a18e83afdd5f4fbe1 100644 +index c26bf04abe86b566e7f5cd29191a0a853f9808f8..4c172e2aee3e48d42009cd39b28f694aa71e20e3 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -132,6 +132,7 @@ public abstract class PlayerList { diff --git a/leaf-server/minecraft-patches/features/0035-Plazma-Add-missing-purpur-configuration-options.patch b/leaf-server/minecraft-patches/features/0035-Plazma-Add-missing-purpur-configuration-options.patch index 6cf55662..c8a6c4a3 100644 --- a/leaf-server/minecraft-patches/features/0035-Plazma-Add-missing-purpur-configuration-options.patch +++ b/leaf-server/minecraft-patches/features/0035-Plazma-Add-missing-purpur-configuration-options.patch @@ -243,7 +243,7 @@ index b230955ae880d84fde40b4feffa5caf3c4449eb7..c8bdfaaf41e2309e4865806b42677064 @Override diff --git a/org/purpurmc/purpur/PurpurConfig.java b/org/purpurmc/purpur/PurpurConfig.java -index bbfd05509dfc2ee453f847d299b3d261324b6fa9..b8c8806789bd0060cd3faee5815bbf25c8715a9b 100644 +index 1cc5c37ee9aab6b9eb45881dddc03715bd4153b7..c0cde1461c0e3ec63bf77cc5056f8c637f307d4a 100644 --- a/org/purpurmc/purpur/PurpurConfig.java +++ b/org/purpurmc/purpur/PurpurConfig.java @@ -322,6 +322,7 @@ public class PurpurConfig { @@ -254,7 +254,7 @@ index bbfd05509dfc2ee453f847d299b3d261324b6fa9..b8c8806789bd0060cd3faee5815bbf25 public static boolean enderChestSixRows = false; public static boolean enderChestPermissionRows = false; public static boolean cryingObsidianValidForPortalFrame = false; -@@ -364,6 +365,7 @@ public class PurpurConfig { +@@ -365,6 +366,7 @@ public class PurpurConfig { case 1 -> 9; default -> 27; }); diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index ea4b7ede..5d23ec55 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -218,10 +218,10 @@ index d8298c7925e3bcea07ead4d438478cc51abcfa16..75670751064add901c2628d53d802835 attributesToSync.clear(); diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 0290e1f0c45677d337f77a0c8269894b32a43ca9..df7e7833890fab9fd63afa93903f2c30abbfd520 100644 +index d6ebc25dc5f04194edde5ad3a1166113e5542a1d..49cbdf014d0626b36eb4c451b6de09508822b7fd 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -2496,7 +2496,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -2522,7 +2522,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @Override public LevelEntityGetter getEntities() { @@ -230,7 +230,7 @@ index 0290e1f0c45677d337f77a0c8269894b32a43ca9..df7e7833890fab9fd63afa93903f2c30 return this.moonrise$getEntityLookup(); // Paper - rewrite chunk system } -@@ -2728,7 +2728,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -2754,7 +2754,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } map.carriedByPlayers.remove(player); diff --git a/leaf-server/minecraft-patches/features/0105-Cache-chunk-key.patch b/leaf-server/minecraft-patches/features/0105-Cache-chunk-key.patch index a9932a9c..07654988 100644 --- a/leaf-server/minecraft-patches/features/0105-Cache-chunk-key.patch +++ b/leaf-server/minecraft-patches/features/0105-Cache-chunk-key.patch @@ -84,7 +84,7 @@ index 571db5f9bf94745a8afe2cd313e593fb15db5e37..1487b7d8be435b3fbad2aabd05796965 valueInMap = new ServerChunkTasks( keyInMap, ServerLightQueue.this.lightInterface, ServerLightQueue.this, priority diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 5423d8228c1da56135ae32b958f432d5b94707ed..95bed1e67758543a7aec12eee1229ee2c4057c88 100644 +index 49cbdf014d0626b36eb4c451b6de09508822b7fd..a9e7424bb55266c5e04c56dcf598ce7d149eeb21 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -508,7 +508,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -96,7 +96,7 @@ index 5423d8228c1da56135ae32b958f432d5b94707ed..95bed1e67758543a7aec12eee1229ee2 return; } -@@ -2569,7 +2569,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -2595,7 +2595,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe public boolean isNaturalSpawningAllowed(ChunkPos chunkPos) { // Paper start - rewrite chunk system diff --git a/leaf-server/minecraft-patches/features/0139-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0139-SparklyPaper-Parallel-world-ticking.patch index 6d404852..68416232 100644 --- a/leaf-server/minecraft-patches/features/0139-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0139-SparklyPaper-Parallel-world-ticking.patch @@ -591,7 +591,7 @@ index d4048661575ebfaf128ba25da365843774364e0e..33dd16a26edd2974f04d9a868d3e58e8 // Gale start - Pufferfish - SIMD support diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 5da07e22ef9dac7baca9d8450b7eae3f6fa141b1..4ecb9a4125233f91379fd2792112aca6bbb3e33f 100644 +index ae5d3de44fb710b48fdabf04f5e706df1f9889b7..31abf2da10bc9b4b7825ed4b3d4e9da52feb2e39 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -182,7 +182,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -619,7 +619,7 @@ index 5da07e22ef9dac7baca9d8450b7eae3f6fa141b1..4ecb9a4125233f91379fd2792112aca6 } // Paper start -@@ -1287,9 +1289,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1313,9 +1315,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe fluidState.tick(this, pos, blockState); } // Paper start - rewrite chunk system @@ -634,7 +634,7 @@ index 5da07e22ef9dac7baca9d8450b7eae3f6fa141b1..4ecb9a4125233f91379fd2792112aca6 // Paper end - rewrite chunk system } -@@ -1300,9 +1305,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1326,9 +1331,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe blockState.tick(this, pos, this.random); } // Paper start - rewrite chunk system @@ -649,7 +649,7 @@ index 5da07e22ef9dac7baca9d8450b7eae3f6fa141b1..4ecb9a4125233f91379fd2792112aca6 // Paper end - rewrite chunk system } -@@ -1553,6 +1561,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1579,6 +1587,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } private void addPlayer(ServerPlayer player) { @@ -658,7 +658,7 @@ index 5da07e22ef9dac7baca9d8450b7eae3f6fa141b1..4ecb9a4125233f91379fd2792112aca6 Entity entity = this.getEntities().get(player.getUUID()); if (entity != null) { LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID()); -@@ -1565,7 +1575,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1591,7 +1601,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // CraftBukkit start private boolean addEntity(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) { @@ -727,7 +727,7 @@ index e2b15968e89a532ec21c786f41b7f9322fd65a04..915498dcb08a678256e6cc1659642d61 // Paper end - Inventory close reason this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index f59662da0bbfe0e768c4ac5c7491d13263ac5cac..bc412a62184757547140860617b8845263905710 100644 +index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..0b8b4658dbbad1bacc13e97b4fc0cdcea7e36a06 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -252,6 +252,8 @@ public abstract class PlayerList { diff --git a/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch b/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch index 26010e8a..b2758401 100644 --- a/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch +++ b/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Purpur Server Paper Changes Original license: MIT Original project: https://github.com/PurpurMC/Purpur -Commit: 96f0ee1ea36729daa5cf6265c9cbae2849bcfd3b +Commit: 51aafbc731b33dfebc4b6180970570cfbbd14d3c Patches listed below are removed in this patch, They exists in Gale or Leaf: * "Rebrand.patch" From cb6ebf06b0e049fc74246fc453ed70505dd800ec Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:20:22 -0400 Subject: [PATCH 20/27] Fix releases --- scripts/prepareRelease.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prepareRelease.sh b/scripts/prepareRelease.sh index c38cf43c..39a70ce5 100755 --- a/scripts/prepareRelease.sh +++ b/scripts/prepareRelease.sh @@ -92,7 +92,7 @@ fi # Delete current release tag if git show-ref --tags $CURRENT_TAG --quiet; then { - git push --delete origin $CURRENT_TAG + gh release delete $CURRENT_TAG --cleanup-tag -y -R "${GITHUB_REPO}" } fi echo "🚀Ready for release" From 7eb3c4f5de9037d334950d55d4268059257ab00c Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:21:10 -0400 Subject: [PATCH 21/27] Update Spark --- leaf-server/build.gradle.kts.patch | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/leaf-server/build.gradle.kts.patch b/leaf-server/build.gradle.kts.patch index faad205d..4d96a330 100644 --- a/leaf-server/build.gradle.kts.patch +++ b/leaf-server/build.gradle.kts.patch @@ -165,10 +165,12 @@ implementation("net.neoforged:srgutils:1.0.9") // Mappings handling implementation("net.neoforged:AutoRenamingTool:2.0.3") // Remap plugins -@@ -203,6 +_,8 @@ +@@ -202,7 +_,9 @@ + // Spark implementation("me.lucko:spark-api:0.1-20240720.200737-2") - implementation("me.lucko:spark-paper:1.10.119-20241121.092015-1") +- implementation("me.lucko:spark-paper:1.10.119-20241121.092015-1") ++ implementation("me.lucko:spark-paper:1.10.133-20250413.112336-1") + + implementation("io.netty:netty-all:4.1.119.Final") // Leaf - Bump Dependencies // Dreeam TODO - Update to 4.2.0 } From b09978b64be46cd5331c829618a7fe8c0a12aeb8 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Mon, 14 Apr 2025 15:52:49 -0400 Subject: [PATCH 22/27] Fix API publish --- .github/workflows/build-1214.yml | 26 +++++++------------------- build.gradle.kts | 2 ++ 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build-1214.yml b/.github/workflows/build-1214.yml index f2c423fb..6699a5bb 100644 --- a/.github/workflows/build-1214.yml +++ b/.github/workflows/build-1214.yml @@ -30,7 +30,7 @@ jobs: ~/.gradle/configuration-cache-* .gradle/patchCache .gradle/patched - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'buildSrc/**/*.kt', 'patches/**/*.patch') }} + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'patches/**/*.patch') }} restore-keys: | ${{ runner.os }}-gradle- @@ -60,10 +60,9 @@ jobs: echo "org.gradle.caching=true" >> ~/.gradle/gradle.properties echo "org.gradle.configuration-cache=true" >> ~/.gradle/gradle.properties echo "org.gradle.configuration-cache.problems=warn" >> ~/.gradle/gradle.properties - echo "org.gradle.jvmargs=${{ env.GRADLE_MEMORY }} -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8" >> ~/.gradle/gradle.properties + echo "org.gradle.jvmargs=${{ env.GRADLE_MEMORY }} -Dfile.encoding=UTF-8" >> ~/.gradle/gradle.properties echo "org.gradle.parallel=true" >> ~/.gradle/gradle.properties echo "org.gradle.daemon=true" >> ~/.gradle/gradle.properties - echo "kotlin.incremental=true" >> ~/.gradle/gradle.properties - name: Apply patches run: | @@ -75,20 +74,16 @@ jobs: --stacktrace --parallel \ --max-workers=$PARALLELISM \ --build-cache \ - --no-configuration-cache \ --no-daemon - name: Create MojmapPaperclipJar - run: ./gradlew createMojmapPaperclipJar --stacktrace --parallel --no-daemon --no-configuration-cache + run: ./gradlew createMojmapPaperclipJar --stacktrace --parallel --no-daemon - - name: Start API Publishing - id: publish-api + - name: Publish API + continue-on-error: true run: | - ( - ./gradlew publish --parallel --max-workers=4 > publish_api_log.txt 2>&1 - echo "API_PUBLISH_DONE=true" >> $GITHUB_ENV - ) & - echo "PUBLISH_PID=$!" >> $GITHUB_ENV + ./gradlew :leaf-api:publish + ./gradlew publishDevBundlePublicationToLeafRepository -PpublishDevBundle=true env: REPO_USER: ${{ secrets.REPO_USER }} REPO_PASSWORD: ${{ secrets.REPO_PASSWORD }} @@ -112,13 +107,6 @@ jobs: GITHUB_REPO: ${{ github.repository }} BUILD_NUMBER: ${{ env.BUILD_NUMBER }} - - name: Wait for API Publishing - run: | - if ps -p ${{ env.PUBLISH_PID }} > /dev/null; then - wait ${{ env.PUBLISH_PID }} || true - fi - grep -i "error" publish_api_log.txt && echo "Found errors in log" || echo "No errors found" - - name: Release Leaf uses: softprops/action-gh-release@master with: diff --git a/build.gradle.kts b/build.gradle.kts index b49ba44d..665437fb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -50,7 +50,9 @@ subprojects { events(TestLogEvent.STANDARD_OUT) } } +} +allprojects { extensions.configure { repositories { maven(leafMavenPublicUrl) { From 14813809e777503f82482e7d421563d58712b315 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:14:14 -0400 Subject: [PATCH 23/27] Where can I find these behavior --- .github/workflows/build-1214.yml | 3 +++ build.gradle.kts | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-1214.yml b/.github/workflows/build-1214.yml index 6699a5bb..24e62557 100644 --- a/.github/workflows/build-1214.yml +++ b/.github/workflows/build-1214.yml @@ -63,6 +63,9 @@ jobs: echo "org.gradle.jvmargs=${{ env.GRADLE_MEMORY }} -Dfile.encoding=UTF-8" >> ~/.gradle/gradle.properties echo "org.gradle.parallel=true" >> ~/.gradle/gradle.properties echo "org.gradle.daemon=true" >> ~/.gradle/gradle.properties + # This is only need if we create custom gradle.properies in home directory + echo "leafUsername=${{ secrets.REPO_USER }}" >> ~/.gradle/gradle.properties + echo "leafPassword=${{ secrets.REPO_PASSWORD }}" >> ~/.gradle/gradle.properties - name: Apply patches run: | diff --git a/build.gradle.kts b/build.gradle.kts index 665437fb..b49ba44d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -50,9 +50,7 @@ subprojects { events(TestLogEvent.STANDARD_OUT) } } -} -allprojects { extensions.configure { repositories { maven(leafMavenPublicUrl) { From 502701329c80adb1eb57c89af577c8ebed16e4c2 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:28:16 -0400 Subject: [PATCH 24/27] Drop ShreddedPaper: Don't block main thread in Connection#syncAfterConfigurationChange & Move to TODO --- ...ShreddedPaper-Don-t-block-main-thread-in-Connection-.patch | 0 ...se-ensureCapacity-to-pre-populate-the-size-of-ticki.patch} | 0 ...irectly-use-the-pre-filtered-ticking-chunks-list-as.patch} | 0 ...-Bulk-writes-to-writeLongArray-during-chunk-loading.patch} | 0 .../{0128-Optimize-AABB.patch => 0127-Optimize-AABB.patch} | 0 ...Set.patch => 0128-Improve-sorting-in-SortedArraySet.patch} | 0 ...-faster.patch => 0129-Make-removeIf-slightly-faster.patch} | 0 ...-LinearPalette.patch => 0130-Optimize-LinearPalette.patch} | 0 ...write.patch => 0131-Slightly-optimized-VarInt-write.patch} | 0 ...ch => 0132-Rewrite-ClientboundLightUpdatePacketData.patch} | 0 ...=> 0133-Some-Optimizations-on-SerializableChunkData.patch} | 0 ...lderManager.patch => 0134-Rework-ChunkHolderManager.patch} | 0 ...mize-chunkUnload.patch => 0135-Optimize-chunkUnload.patch} | 0 ...ync-chunk-sending.patch => 0136-Async-chunk-sending.patch} | 0 ...Configurations.patch => 0137-Spawner-Configurations.patch} | 0 ...g.patch => 0138-SparklyPaper-Parallel-world-ticking.patch} | 0 ...PT.patch => 0139-SparklyPaper-Track-each-world-MSPT.patch} | 2 +- ...aperPR-Fix-cancelled-Projectile-Events-still-consum.patch} | 0 ...ptimize-SetLookAndInteract-and-NearestVisibleLiving.patch} | 0 ....patch => 0142-Remove-streams-on-InsideBrownianWalk.patch} | 0 ...eDistance.patch => 0143-Use-BFS-on-getSlopeDistance.patch} | 0 ...tch => 0144-Paper-PR-Throttle-failed-spawn-attempts.patch} | 0 ...=> 0145-Improve-BlockEntity-ticking-isRemoved-check.patch} | 0 ...ion.patch => 0146-Raytrace-AntiXray-SDK-integration.patch} | 0 ...patch => 0147-Optimize-addOrUpdateTransientModifier.patch} | 0 ...Map.create.patch => 0148-Optimize-ContextMap.create.patch} | 0 ...k.patch => 0149-Micro-optimizations-for-random-tick.patch} | 2 +- ...Remove-streams-on-updateConnectedPlayersWithinRange.patch} | 0 ...ctor.patch => 0151-Remove-streams-on-PlayerDetector.patch} | 0 ...ync-Block-Finding.patch => 0152-Async-Block-Finding.patch} | 0 ....patch => 0153-Use-direct-iteration-on-Sensing.tick.patch} | 0 ...ing.patch => 0154-Optimise-non-flush-packet-sending.patch} | 4 ++-- ...revent-double-chunk-retrieving-in-entity-fluid-push.patch} | 0 ...AsyncTargetFinding.patch => 0156-AsyncTargetFinding.patch} | 0 ...er.patch => 0157-Null-handling-on-MultifaceSpreader.patch} | 0 ...-virtual-threads.patch => 0158-More-virtual-threads.patch} | 0 36 files changed, 4 insertions(+), 4 deletions(-) rename {leaf-server/minecraft-patches/features => leaf-archived-patches/work/server}/0124-ShreddedPaper-Don-t-block-main-thread-in-Connection-.patch (100%) rename leaf-server/minecraft-patches/features/{0125-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch => 0124-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch} (100%) rename leaf-server/minecraft-patches/features/{0126-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch => 0125-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch} (100%) rename leaf-server/minecraft-patches/features/{0127-Bulk-writes-to-writeLongArray-during-chunk-loading.patch => 0126-Bulk-writes-to-writeLongArray-during-chunk-loading.patch} (100%) rename leaf-server/minecraft-patches/features/{0128-Optimize-AABB.patch => 0127-Optimize-AABB.patch} (100%) rename leaf-server/minecraft-patches/features/{0129-Improve-sorting-in-SortedArraySet.patch => 0128-Improve-sorting-in-SortedArraySet.patch} (100%) rename leaf-server/minecraft-patches/features/{0130-Make-removeIf-slightly-faster.patch => 0129-Make-removeIf-slightly-faster.patch} (100%) rename leaf-server/minecraft-patches/features/{0131-Optimize-LinearPalette.patch => 0130-Optimize-LinearPalette.patch} (100%) rename leaf-server/minecraft-patches/features/{0132-Slightly-optimized-VarInt-write.patch => 0131-Slightly-optimized-VarInt-write.patch} (100%) rename leaf-server/minecraft-patches/features/{0133-Rewrite-ClientboundLightUpdatePacketData.patch => 0132-Rewrite-ClientboundLightUpdatePacketData.patch} (100%) rename leaf-server/minecraft-patches/features/{0134-Some-Optimizations-on-SerializableChunkData.patch => 0133-Some-Optimizations-on-SerializableChunkData.patch} (100%) rename leaf-server/minecraft-patches/features/{0135-Rework-ChunkHolderManager.patch => 0134-Rework-ChunkHolderManager.patch} (100%) rename leaf-server/minecraft-patches/features/{0136-Optimize-chunkUnload.patch => 0135-Optimize-chunkUnload.patch} (100%) rename leaf-server/minecraft-patches/features/{0137-Async-chunk-sending.patch => 0136-Async-chunk-sending.patch} (100%) rename leaf-server/minecraft-patches/features/{0138-Spawner-Configurations.patch => 0137-Spawner-Configurations.patch} (100%) rename leaf-server/minecraft-patches/features/{0139-SparklyPaper-Parallel-world-ticking.patch => 0138-SparklyPaper-Parallel-world-ticking.patch} (100%) rename leaf-server/minecraft-patches/features/{0140-SparklyPaper-Track-each-world-MSPT.patch => 0139-SparklyPaper-Track-each-world-MSPT.patch} (98%) rename leaf-server/minecraft-patches/features/{0141-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch => 0140-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch} (100%) rename leaf-server/minecraft-patches/features/{0142-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch => 0141-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch} (100%) rename leaf-server/minecraft-patches/features/{0143-Remove-streams-on-InsideBrownianWalk.patch => 0142-Remove-streams-on-InsideBrownianWalk.patch} (100%) rename leaf-server/minecraft-patches/features/{0144-Use-BFS-on-getSlopeDistance.patch => 0143-Use-BFS-on-getSlopeDistance.patch} (100%) rename leaf-server/minecraft-patches/features/{0145-Paper-PR-Throttle-failed-spawn-attempts.patch => 0144-Paper-PR-Throttle-failed-spawn-attempts.patch} (100%) rename leaf-server/minecraft-patches/features/{0146-Improve-BlockEntity-ticking-isRemoved-check.patch => 0145-Improve-BlockEntity-ticking-isRemoved-check.patch} (100%) rename leaf-server/minecraft-patches/features/{0147-Raytrace-AntiXray-SDK-integration.patch => 0146-Raytrace-AntiXray-SDK-integration.patch} (100%) rename leaf-server/minecraft-patches/features/{0148-Optimize-addOrUpdateTransientModifier.patch => 0147-Optimize-addOrUpdateTransientModifier.patch} (100%) rename leaf-server/minecraft-patches/features/{0149-Optimize-ContextMap.create.patch => 0148-Optimize-ContextMap.create.patch} (100%) rename leaf-server/minecraft-patches/features/{0150-Micro-optimizations-for-random-tick.patch => 0149-Micro-optimizations-for-random-tick.patch} (97%) rename leaf-server/minecraft-patches/features/{0151-Remove-streams-on-updateConnectedPlayersWithinRange.patch => 0150-Remove-streams-on-updateConnectedPlayersWithinRange.patch} (100%) rename leaf-server/minecraft-patches/features/{0152-Remove-streams-on-PlayerDetector.patch => 0151-Remove-streams-on-PlayerDetector.patch} (100%) rename leaf-server/minecraft-patches/features/{0153-Async-Block-Finding.patch => 0152-Async-Block-Finding.patch} (100%) rename leaf-server/minecraft-patches/features/{0154-Use-direct-iteration-on-Sensing.tick.patch => 0153-Use-direct-iteration-on-Sensing.tick.patch} (100%) rename leaf-server/minecraft-patches/features/{0155-Optimise-non-flush-packet-sending.patch => 0154-Optimise-non-flush-packet-sending.patch} (94%) rename leaf-server/minecraft-patches/features/{0156-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch => 0155-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch} (100%) rename leaf-server/minecraft-patches/features/{0157-AsyncTargetFinding.patch => 0156-AsyncTargetFinding.patch} (100%) rename leaf-server/minecraft-patches/features/{0158-Null-handling-on-MultifaceSpreader.patch => 0157-Null-handling-on-MultifaceSpreader.patch} (100%) rename leaf-server/minecraft-patches/features/{0159-More-virtual-threads.patch => 0158-More-virtual-threads.patch} (100%) diff --git a/leaf-server/minecraft-patches/features/0124-ShreddedPaper-Don-t-block-main-thread-in-Connection-.patch b/leaf-archived-patches/work/server/0124-ShreddedPaper-Don-t-block-main-thread-in-Connection-.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0124-ShreddedPaper-Don-t-block-main-thread-in-Connection-.patch rename to leaf-archived-patches/work/server/0124-ShreddedPaper-Don-t-block-main-thread-in-Connection-.patch diff --git a/leaf-server/minecraft-patches/features/0125-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch b/leaf-server/minecraft-patches/features/0124-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0125-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch rename to leaf-server/minecraft-patches/features/0124-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch diff --git a/leaf-server/minecraft-patches/features/0126-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch b/leaf-server/minecraft-patches/features/0125-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0126-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch rename to leaf-server/minecraft-patches/features/0125-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch diff --git a/leaf-server/minecraft-patches/features/0127-Bulk-writes-to-writeLongArray-during-chunk-loading.patch b/leaf-server/minecraft-patches/features/0126-Bulk-writes-to-writeLongArray-during-chunk-loading.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0127-Bulk-writes-to-writeLongArray-during-chunk-loading.patch rename to leaf-server/minecraft-patches/features/0126-Bulk-writes-to-writeLongArray-during-chunk-loading.patch diff --git a/leaf-server/minecraft-patches/features/0128-Optimize-AABB.patch b/leaf-server/minecraft-patches/features/0127-Optimize-AABB.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0128-Optimize-AABB.patch rename to leaf-server/minecraft-patches/features/0127-Optimize-AABB.patch diff --git a/leaf-server/minecraft-patches/features/0129-Improve-sorting-in-SortedArraySet.patch b/leaf-server/minecraft-patches/features/0128-Improve-sorting-in-SortedArraySet.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0129-Improve-sorting-in-SortedArraySet.patch rename to leaf-server/minecraft-patches/features/0128-Improve-sorting-in-SortedArraySet.patch diff --git a/leaf-server/minecraft-patches/features/0130-Make-removeIf-slightly-faster.patch b/leaf-server/minecraft-patches/features/0129-Make-removeIf-slightly-faster.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0130-Make-removeIf-slightly-faster.patch rename to leaf-server/minecraft-patches/features/0129-Make-removeIf-slightly-faster.patch diff --git a/leaf-server/minecraft-patches/features/0131-Optimize-LinearPalette.patch b/leaf-server/minecraft-patches/features/0130-Optimize-LinearPalette.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0131-Optimize-LinearPalette.patch rename to leaf-server/minecraft-patches/features/0130-Optimize-LinearPalette.patch diff --git a/leaf-server/minecraft-patches/features/0132-Slightly-optimized-VarInt-write.patch b/leaf-server/minecraft-patches/features/0131-Slightly-optimized-VarInt-write.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0132-Slightly-optimized-VarInt-write.patch rename to leaf-server/minecraft-patches/features/0131-Slightly-optimized-VarInt-write.patch diff --git a/leaf-server/minecraft-patches/features/0133-Rewrite-ClientboundLightUpdatePacketData.patch b/leaf-server/minecraft-patches/features/0132-Rewrite-ClientboundLightUpdatePacketData.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0133-Rewrite-ClientboundLightUpdatePacketData.patch rename to leaf-server/minecraft-patches/features/0132-Rewrite-ClientboundLightUpdatePacketData.patch diff --git a/leaf-server/minecraft-patches/features/0134-Some-Optimizations-on-SerializableChunkData.patch b/leaf-server/minecraft-patches/features/0133-Some-Optimizations-on-SerializableChunkData.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0134-Some-Optimizations-on-SerializableChunkData.patch rename to leaf-server/minecraft-patches/features/0133-Some-Optimizations-on-SerializableChunkData.patch diff --git a/leaf-server/minecraft-patches/features/0135-Rework-ChunkHolderManager.patch b/leaf-server/minecraft-patches/features/0134-Rework-ChunkHolderManager.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0135-Rework-ChunkHolderManager.patch rename to leaf-server/minecraft-patches/features/0134-Rework-ChunkHolderManager.patch diff --git a/leaf-server/minecraft-patches/features/0136-Optimize-chunkUnload.patch b/leaf-server/minecraft-patches/features/0135-Optimize-chunkUnload.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0136-Optimize-chunkUnload.patch rename to leaf-server/minecraft-patches/features/0135-Optimize-chunkUnload.patch diff --git a/leaf-server/minecraft-patches/features/0137-Async-chunk-sending.patch b/leaf-server/minecraft-patches/features/0136-Async-chunk-sending.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0137-Async-chunk-sending.patch rename to leaf-server/minecraft-patches/features/0136-Async-chunk-sending.patch diff --git a/leaf-server/minecraft-patches/features/0138-Spawner-Configurations.patch b/leaf-server/minecraft-patches/features/0137-Spawner-Configurations.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0138-Spawner-Configurations.patch rename to leaf-server/minecraft-patches/features/0137-Spawner-Configurations.patch diff --git a/leaf-server/minecraft-patches/features/0139-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0138-SparklyPaper-Parallel-world-ticking.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0139-SparklyPaper-Parallel-world-ticking.patch rename to leaf-server/minecraft-patches/features/0138-SparklyPaper-Parallel-world-ticking.patch diff --git a/leaf-server/minecraft-patches/features/0140-SparklyPaper-Track-each-world-MSPT.patch b/leaf-server/minecraft-patches/features/0139-SparklyPaper-Track-each-world-MSPT.patch similarity index 98% rename from leaf-server/minecraft-patches/features/0140-SparklyPaper-Track-each-world-MSPT.patch rename to leaf-server/minecraft-patches/features/0139-SparklyPaper-Track-each-world-MSPT.patch index 2fb7a7cb..aef67f40 100644 --- a/leaf-server/minecraft-patches/features/0140-SparklyPaper-Track-each-world-MSPT.patch +++ b/leaf-server/minecraft-patches/features/0139-SparklyPaper-Track-each-world-MSPT.patch @@ -27,7 +27,7 @@ index ac751d460ae0c8dbb858c4047c459a11b57ae175..24926aa7ed5c78b235659daf18b224b1 CrashReport crashReport = CrashReport.forThrowable(levelTickingException, "Exception ticking world"); serverLevel.fillReportDetails(crashReport); diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 4ecb9a4125233f91379fd2792112aca6bbb3e33f..b5a61261083ddab70582c1a1d5cac0b9ced9b652 100644 +index 31abf2da10bc9b4b7825ed4b3d4e9da52feb2e39..9ba1c29b75ba0eb545097eef4fe568c53ebd885c 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -573,6 +573,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe diff --git a/leaf-server/minecraft-patches/features/0141-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch b/leaf-server/minecraft-patches/features/0140-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0141-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch rename to leaf-server/minecraft-patches/features/0140-PaperPR-Fix-cancelled-Projectile-Events-still-consum.patch diff --git a/leaf-server/minecraft-patches/features/0142-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch b/leaf-server/minecraft-patches/features/0141-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0142-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch rename to leaf-server/minecraft-patches/features/0141-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch diff --git a/leaf-server/minecraft-patches/features/0143-Remove-streams-on-InsideBrownianWalk.patch b/leaf-server/minecraft-patches/features/0142-Remove-streams-on-InsideBrownianWalk.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0143-Remove-streams-on-InsideBrownianWalk.patch rename to leaf-server/minecraft-patches/features/0142-Remove-streams-on-InsideBrownianWalk.patch diff --git a/leaf-server/minecraft-patches/features/0144-Use-BFS-on-getSlopeDistance.patch b/leaf-server/minecraft-patches/features/0143-Use-BFS-on-getSlopeDistance.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0144-Use-BFS-on-getSlopeDistance.patch rename to leaf-server/minecraft-patches/features/0143-Use-BFS-on-getSlopeDistance.patch diff --git a/leaf-server/minecraft-patches/features/0145-Paper-PR-Throttle-failed-spawn-attempts.patch b/leaf-server/minecraft-patches/features/0144-Paper-PR-Throttle-failed-spawn-attempts.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0145-Paper-PR-Throttle-failed-spawn-attempts.patch rename to leaf-server/minecraft-patches/features/0144-Paper-PR-Throttle-failed-spawn-attempts.patch diff --git a/leaf-server/minecraft-patches/features/0146-Improve-BlockEntity-ticking-isRemoved-check.patch b/leaf-server/minecraft-patches/features/0145-Improve-BlockEntity-ticking-isRemoved-check.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0146-Improve-BlockEntity-ticking-isRemoved-check.patch rename to leaf-server/minecraft-patches/features/0145-Improve-BlockEntity-ticking-isRemoved-check.patch diff --git a/leaf-server/minecraft-patches/features/0147-Raytrace-AntiXray-SDK-integration.patch b/leaf-server/minecraft-patches/features/0146-Raytrace-AntiXray-SDK-integration.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0147-Raytrace-AntiXray-SDK-integration.patch rename to leaf-server/minecraft-patches/features/0146-Raytrace-AntiXray-SDK-integration.patch diff --git a/leaf-server/minecraft-patches/features/0148-Optimize-addOrUpdateTransientModifier.patch b/leaf-server/minecraft-patches/features/0147-Optimize-addOrUpdateTransientModifier.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0148-Optimize-addOrUpdateTransientModifier.patch rename to leaf-server/minecraft-patches/features/0147-Optimize-addOrUpdateTransientModifier.patch diff --git a/leaf-server/minecraft-patches/features/0149-Optimize-ContextMap.create.patch b/leaf-server/minecraft-patches/features/0148-Optimize-ContextMap.create.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0149-Optimize-ContextMap.create.patch rename to leaf-server/minecraft-patches/features/0148-Optimize-ContextMap.create.patch diff --git a/leaf-server/minecraft-patches/features/0150-Micro-optimizations-for-random-tick.patch b/leaf-server/minecraft-patches/features/0149-Micro-optimizations-for-random-tick.patch similarity index 97% rename from leaf-server/minecraft-patches/features/0150-Micro-optimizations-for-random-tick.patch rename to leaf-server/minecraft-patches/features/0149-Micro-optimizations-for-random-tick.patch index 3c67a41e..e2a28692 100644 --- a/leaf-server/minecraft-patches/features/0150-Micro-optimizations-for-random-tick.patch +++ b/leaf-server/minecraft-patches/features/0149-Micro-optimizations-for-random-tick.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Micro optimizations for random tick diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index b5a61261083ddab70582c1a1d5cac0b9ced9b652..ab84d6a07c094b9fb23cd63504d7652342fd6974 100644 +index 9ba1c29b75ba0eb545097eef4fe568c53ebd885c..90bdcd168ad5b1a940f81b191bd59a34d3a33070 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -924,7 +924,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe diff --git a/leaf-server/minecraft-patches/features/0151-Remove-streams-on-updateConnectedPlayersWithinRange.patch b/leaf-server/minecraft-patches/features/0150-Remove-streams-on-updateConnectedPlayersWithinRange.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0151-Remove-streams-on-updateConnectedPlayersWithinRange.patch rename to leaf-server/minecraft-patches/features/0150-Remove-streams-on-updateConnectedPlayersWithinRange.patch diff --git a/leaf-server/minecraft-patches/features/0152-Remove-streams-on-PlayerDetector.patch b/leaf-server/minecraft-patches/features/0151-Remove-streams-on-PlayerDetector.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0152-Remove-streams-on-PlayerDetector.patch rename to leaf-server/minecraft-patches/features/0151-Remove-streams-on-PlayerDetector.patch diff --git a/leaf-server/minecraft-patches/features/0153-Async-Block-Finding.patch b/leaf-server/minecraft-patches/features/0152-Async-Block-Finding.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0153-Async-Block-Finding.patch rename to leaf-server/minecraft-patches/features/0152-Async-Block-Finding.patch diff --git a/leaf-server/minecraft-patches/features/0154-Use-direct-iteration-on-Sensing.tick.patch b/leaf-server/minecraft-patches/features/0153-Use-direct-iteration-on-Sensing.tick.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0154-Use-direct-iteration-on-Sensing.tick.patch rename to leaf-server/minecraft-patches/features/0153-Use-direct-iteration-on-Sensing.tick.patch diff --git a/leaf-server/minecraft-patches/features/0155-Optimise-non-flush-packet-sending.patch b/leaf-server/minecraft-patches/features/0154-Optimise-non-flush-packet-sending.patch similarity index 94% rename from leaf-server/minecraft-patches/features/0155-Optimise-non-flush-packet-sending.patch rename to leaf-server/minecraft-patches/features/0154-Optimise-non-flush-packet-sending.patch index f15568e7..137d91b4 100644 --- a/leaf-server/minecraft-patches/features/0155-Optimise-non-flush-packet-sending.patch +++ b/leaf-server/minecraft-patches/features/0154-Optimise-non-flush-packet-sending.patch @@ -26,7 +26,7 @@ Locally this patch drops the entity tracker tick by a full 1.5x. Co-authored-by: Quang Tran <3d7777456@gmail.com> diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index 5b46036868b6c9d082e35591e58735e16adaae62..9563165c945757996da11f55e2221e620dd93327 100644 +index 00a82873d226f113278632a53c0faca420dd67d4..f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java @@ -147,6 +147,7 @@ public class Connection extends SimpleChannelInboundHandler> { @@ -45,7 +45,7 @@ index 5b46036868b6c9d082e35591e58735e16adaae62..9563165c945757996da11f55e2221e62 this.address = this.channel.remoteAddress(); this.preparing = false; // Spigot if (this.delayedDisconnect != null) { -@@ -477,6 +479,11 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -476,6 +478,11 @@ public class Connection extends SimpleChannelInboundHandler> { if (this.channel.eventLoop().inEventLoop()) { this.doSendPacket(packet, sendListener, flush); } else { diff --git a/leaf-server/minecraft-patches/features/0156-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch b/leaf-server/minecraft-patches/features/0155-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0156-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch rename to leaf-server/minecraft-patches/features/0155-Prevent-double-chunk-retrieving-in-entity-fluid-push.patch diff --git a/leaf-server/minecraft-patches/features/0157-AsyncTargetFinding.patch b/leaf-server/minecraft-patches/features/0156-AsyncTargetFinding.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0157-AsyncTargetFinding.patch rename to leaf-server/minecraft-patches/features/0156-AsyncTargetFinding.patch diff --git a/leaf-server/minecraft-patches/features/0158-Null-handling-on-MultifaceSpreader.patch b/leaf-server/minecraft-patches/features/0157-Null-handling-on-MultifaceSpreader.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0158-Null-handling-on-MultifaceSpreader.patch rename to leaf-server/minecraft-patches/features/0157-Null-handling-on-MultifaceSpreader.patch diff --git a/leaf-server/minecraft-patches/features/0159-More-virtual-threads.patch b/leaf-server/minecraft-patches/features/0158-More-virtual-threads.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0159-More-virtual-threads.patch rename to leaf-server/minecraft-patches/features/0158-More-virtual-threads.patch From dff5b11bd9b7acb29293da131ed4da7d9903d707 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Thu, 17 Apr 2025 02:45:56 -0400 Subject: [PATCH 25/27] cleanup --- ...-SparklyPaper-Parallel-world-ticking.patch | 87 +++++++------------ .../0143-Use-BFS-on-getSlopeDistance.patch | 60 ++++++------- .../features/0152-Async-Block-Finding.patch | 52 ++++------- ....patch => 0156-Async-Target-Finding.patch} | 71 +++++++-------- ...7-Null-handling-on-MultifaceSpreader.patch | 17 ++-- .../features/0158-More-virtual-threads.patch | 67 +++++--------- .../0033-Async-playerdata-saving.patch | 8 +- scripts/prepareRelease.sh | 11 +++ 8 files changed, 147 insertions(+), 226 deletions(-) rename leaf-server/minecraft-patches/features/{0156-AsyncTargetFinding.patch => 0156-Async-Target-Finding.patch} (86%) diff --git a/leaf-server/minecraft-patches/features/0138-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0138-SparklyPaper-Parallel-world-ticking.patch index 68416232..698bc071 100644 --- a/leaf-server/minecraft-patches/features/0138-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0138-SparklyPaper-Parallel-world-ticking.patch @@ -424,47 +424,41 @@ index c50a301a0c2365c2052aefc6a23fcf6fa82e1b9d..ac751d460ae0c8dbb858c4047c459a11 } // CraftBukkit end diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java -index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..a7e1c9979897a12a7a8f417545ae96f703a1b248 100644 +index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..abe7ffd48766c48fab091947f34db436b3c883d0 100644 --- a/net/minecraft/server/PlayerAdvancements.java +++ b/net/minecraft/server/PlayerAdvancements.java -@@ -19,6 +19,7 @@ import java.nio.file.Path; - import java.util.HashMap; - import java.util.HashSet; - import java.util.LinkedHashMap; -+import java.util.concurrent.ConcurrentHashMap; - import java.util.Map; - import java.util.Set; - import java.util.Map.Entry; -@@ -53,8 +54,9 @@ public class PlayerAdvancements { +@@ -53,8 +53,11 @@ public class PlayerAdvancements { private AdvancementTree tree; private final Map progress = new LinkedHashMap<>(); private final Set visible = new HashSet<>(); - private final Set progressChanged = new HashSet<>(); - private final Set rootsToUpdate = new HashSet<>(); ++ // Leaf start - SparklyPaper - parallel world ticking + private final Set progressChanged = new HashSet<>(); // Used when PWT is disabled -+ private final Set progressChangedConcurrent = ConcurrentHashMap.newKeySet(); // Used when PWT is enabled ++ private final Set progressChangedConcurrent = java.util.concurrent.ConcurrentHashMap.newKeySet(); // Used when PWT is enabled + private final Set rootsToUpdate = new HashSet<>(); // Always managed on player tick thread ++ // Leaf end - SparklyPaper - parallel world ticking private ServerPlayer player; @Nullable private AdvancementHolder lastSelectedTab; -@@ -88,6 +90,8 @@ public class PlayerAdvancements { +@@ -88,6 +91,7 @@ public class PlayerAdvancements { this.visible.clear(); this.rootsToUpdate.clear(); this.progressChanged.clear(); -+ // PWT Fix: Also clear concurrent set on reload -+ this.progressChangedConcurrent.clear(); ++ this.progressChangedConcurrent.clear(); // Leaf - SparklyPaper - parallel world ticking fix - Also clear concurrent set on reload this.isFirstPacket = true; this.lastSelectedTab = null; this.tree = manager.tree(); -@@ -151,6 +155,7 @@ public class PlayerAdvancements { +@@ -151,7 +155,7 @@ public class PlayerAdvancements { if (org.galemc.gale.configuration.GaleGlobalConfiguration.get().logToConsole.ignoredAdvancements) LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", path, this.playerSavePath); // Gale - Purpur - do not log ignored advancements } else { this.startProgress(advancementHolder, progress); -+ // PWT Fix: Always add to non-concurrent set during load, flushDirty will handle sync - this.progressChanged.add(advancementHolder); +- this.progressChanged.add(advancementHolder); ++ this.progressChanged.add(advancementHolder); // Leaf - SparklyPaper - parallel world ticking fix - Always add to non-concurrent set during load, flushDirty will handle sync this.markForVisibilityUpdate(advancementHolder); } -@@ -183,25 +188,25 @@ public class PlayerAdvancements { + }); +@@ -183,10 +187,12 @@ public class PlayerAdvancements { return false; } // Paper end - Add PlayerAdvancementCriterionGrantEvent @@ -472,65 +466,46 @@ index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..a7e1c9979897a12a7a8f417545ae96f7 - this.progressChanged.add(advancement); - flag = true; - if (!isDone && orStartProgress.isDone()) { ++ // Leaf start - SparklyPaper - parallel world ticking + this.unregisterListeners(advancement); // Must unregister criteria listeners + (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.progressChangedConcurrent : this.progressChanged).add(advancement); + flag = true; // Mark progress changed + if (!isDone && orStartProgress.isDone()) { // If the advancement was just completed ++ // Leaf end - SparklyPaper - parallel world ticking // Paper start - Add Adventure message to PlayerAdvancementDoneEvent -- final net.kyori.adventure.text.Component message = advancement.value().display().flatMap(info -> { -- return java.util.Optional.ofNullable( -- info.shouldAnnounceChat() ? io.papermc.paper.adventure.PaperAdventure.asAdventure(info.getType().createAnnouncement(advancement, this.player)) : null -- ); -- }).orElse(null); -- final org.bukkit.event.player.PlayerAdvancementDoneEvent event = new org.bukkit.event.player.PlayerAdvancementDoneEvent(this.player.getBukkitEntity(), advancement.toBukkit(), message); -+ final net.kyori.adventure.text.Component message = advancement.value().display().flatMap(info -> { // Paper - Add Adventure message to PlayerAdvancementDoneEvent -+ return java.util.Optional.ofNullable( // Paper - Add Adventure message to PlayerAdvancementDoneEvent -+ info.shouldAnnounceChat() ? io.papermc.paper.adventure.PaperAdventure.asAdventure(info.getType().createAnnouncement(advancement, this.player)) : null // Paper - Add Adventure message to PlayerAdvancementDoneEvent -+ ); // Paper - Add Adventure message to PlayerAdvancementDoneEvent -+ }).orElse(null); // Paper - Add Adventure message to PlayerAdvancementDoneEvent -+ final org.bukkit.event.player.PlayerAdvancementDoneEvent event = new org.bukkit.event.player.PlayerAdvancementDoneEvent(this.player.getBukkitEntity(), advancement.toBukkit(), message); // Paper - Add Adventure message to PlayerAdvancementDoneEvent - this.player.level().getCraftServer().getPluginManager().callEvent(event); // CraftBukkit - // Paper end - advancement.value().rewards().grant(this.player); - advancement.value().display().ifPresent(displayInfo -> { - // Paper start - Add Adventure message to PlayerAdvancementDoneEvent -- if (event.message() != null && this.player.serverLevel().getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) { -- if (org.purpurmc.purpur.PurpurConfig.advancementOnlyBroadcastToAffectedPlayer) this.player.sendMessage(message); else // Purpur - Configurable broadcast settings -- this.playerList.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false); -+ if (event.message() != null && this.player.serverLevel().getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) { // Paper - Add Adventure message to PlayerAdvancementDoneEvent -+ if (org.purpurmc.purpur.PurpurConfig.advancementOnlyBroadcastToAffectedPlayer) this.player.sendMessage(message); else // Purpur - Configurable broadcast settings // Paper - Add Adventure message to PlayerAdvancementDoneEvent -+ this.playerList.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false); // Paper - Add Adventure message to PlayerAdvancementDoneEvent - // Paper end - } - }); -@@ -220,12 +225,12 @@ public class PlayerAdvancements { + final net.kyori.adventure.text.Component message = advancement.value().display().flatMap(info -> { + return java.util.Optional.ofNullable( +@@ -220,12 +226,14 @@ public class PlayerAdvancements { AdvancementProgress orStartProgress = this.getOrStartProgress(advancement); boolean isDone = orStartProgress.isDone(); if (orStartProgress.revokeProgress(criterionKey)) { - this.registerListeners(advancement); - this.progressChanged.add(advancement); ++ // Leaf start - SparklyPaper - parallel world ticking + this.registerListeners(advancement); // Re-register listeners if it's no longer done + (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.progressChangedConcurrent : this.progressChanged).add(advancement); ++ // Leaf end - SparklyPaper - parallel world ticking flag = true; } - if (isDone && !orStartProgress.isDone()) { -+ if (isDone && !orStartProgress.isDone()) { // If the advancement was just un-completed ++ if (isDone && !orStartProgress.isDone()) { // Leaf - SparklyPaper - parallel world ticking - If the advancement was just un-completed this.markForVisibilityUpdate(advancement); } -@@ -271,7 +276,9 @@ public class PlayerAdvancements { +@@ -271,7 +279,10 @@ public class PlayerAdvancements { } public void flushDirty(ServerPlayer serverPlayer) { - if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !this.progressChanged.isEmpty()) { ++ // Leaf start - SparklyPaper - parallel world ticking + final boolean useConcurrent = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled; + final Set relevantProgressSet = useConcurrent ? this.progressChangedConcurrent : this.progressChanged; + if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !relevantProgressSet.isEmpty()) { Map map = new HashMap<>(); Set set = new java.util.TreeSet<>(java.util.Comparator.comparing(adv -> adv.id().toString())); // Paper - Changed from HashSet to TreeSet ordered alphabetically. Set set1 = new HashSet<>(); -@@ -279,16 +286,23 @@ public class PlayerAdvancements { +@@ -279,16 +290,24 @@ public class PlayerAdvancements { for (AdvancementNode advancementNode : this.rootsToUpdate) { this.updateTreeVisibility(advancementNode, set, set1); } @@ -542,13 +517,11 @@ index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..a7e1c9979897a12a7a8f417545ae96f7 - for (AdvancementHolder advancementHolder : this.progressChanged) { - if (this.visible.contains(advancementHolder)) { -- map.put(advancementHolder.id(), this.progress.get(advancementHolder)); -+ for (AdvancementHolder advancementHolder : toProcess) { -+ if (this.visible.contains(advancementHolder)) { // Only include progress for visible advancements -+ map.put(advancementHolder.id(), this.progress.get(advancementHolder)); -+ } ++ for (AdvancementHolder advancementHolder : toProcess) { ++ if (this.visible.contains(advancementHolder)) { // Only include progress for visible advancements + map.put(advancementHolder.id(), this.progress.get(advancementHolder)); } -- } + } - this.progressChanged.clear(); + if (useConcurrent) { @@ -557,22 +530,26 @@ index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..a7e1c9979897a12a7a8f417545ae96f7 + this.progressChanged.clear(); // Clear the regular set + } + } ++ // Leaf end - SparklyPaper - parallel world ticking if (!map.isEmpty() || !set.isEmpty() || !set1.isEmpty()) { serverPlayer.connection.send(new ClientboundUpdateAdvancementsPacket(this.isFirstPacket, set, set1, map)); } -@@ -331,9 +345,10 @@ public class PlayerAdvancements { +@@ -331,10 +350,13 @@ public class PlayerAdvancements { AdvancementHolder advancementHolder = node.holder(); if (visible) { if (this.visible.add(advancementHolder)) { - advancementOutput.add(advancementHolder); ++ // Leaf start - SparklyPaper - parallel world ticking + advancementOutput.add(advancementHolder); // Add to visible set for packet if (this.progress.containsKey(advancementHolder)) { - this.progressChanged.add(advancementHolder); + // If progress exists, mark it changed so the progress data is sent + (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.progressChangedConcurrent : this.progressChanged).add(advancementHolder); } ++ // Leaf end - SparklyPaper - parallel world ticking } } else if (this.visible.remove(advancementHolder)) { + idOutput.add(advancementHolder.id()); diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java index d4048661575ebfaf128ba25da365843774364e0e..33dd16a26edd2974f04d9a868d3e58e8e3060032 100644 --- a/net/minecraft/server/dedicated/DedicatedServer.java diff --git a/leaf-server/minecraft-patches/features/0143-Use-BFS-on-getSlopeDistance.patch b/leaf-server/minecraft-patches/features/0143-Use-BFS-on-getSlopeDistance.patch index 3532adf9..bdefc5ba 100644 --- a/leaf-server/minecraft-patches/features/0143-Use-BFS-on-getSlopeDistance.patch +++ b/leaf-server/minecraft-patches/features/0143-Use-BFS-on-getSlopeDistance.patch @@ -9,47 +9,23 @@ Leaf: ~48ms (-36%) This should help drastically on the farms that use actively changing fluids. diff --git a/net/minecraft/world/level/material/FlowingFluid.java b/net/minecraft/world/level/material/FlowingFluid.java -index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..33e5c19362de8b4002c23959661535b835eb0ce5 100644 +index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..f6bc70685e846e9114f477dfd8aceca3b910a09f 100644 --- a/net/minecraft/world/level/material/FlowingFluid.java +++ b/net/minecraft/world/level/material/FlowingFluid.java -@@ -8,6 +8,8 @@ import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; - import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; - import java.util.Map; - import java.util.Map.Entry; -+import java.util.Queue; -+ - import net.minecraft.core.BlockPos; - import net.minecraft.core.Direction; - import net.minecraft.server.level.ServerLevel; -@@ -341,31 +343,76 @@ public abstract class FlowingFluid extends Fluid { +@@ -341,32 +341,81 @@ public abstract class FlowingFluid extends Fluid { protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state, BlockPos source) { beforeDestroyingBlock(level, pos, state); } // Paper - Add BlockBreakBlockEvent protected abstract void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state); - protected int getSlopeDistance(LevelReader level, BlockPos pos, int depth, Direction direction, BlockState state, FlowingFluid.SpreadContext spreadContext) { - int i = 1000; ++ // Leaf start - Use BFS on getSlopeDistance + protected int getSlopeDistance(LevelReader level, BlockPos startPos, int initialDepth, Direction excludedDirection, BlockState startState, FlowingFluid.SpreadContext spreadContext) { + it.unimi.dsi.fastutil.longs.LongSet visited = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(512); + java.util.Queue queue = new java.util.ArrayDeque<>(256); - -- for (Direction direction1 : Direction.Plane.HORIZONTAL) { -- if (direction1 != direction) { -- BlockPos blockPos = pos.relative(direction1); -- BlockState blockState = spreadContext.getBlockStateIfLoaded(blockPos); // Paper - Prevent chunk loading from fluid flowing -- if (blockState == null) continue; // Paper - Prevent chunk loading from fluid flowing -- FluidState fluidState = blockState.getFluidState(); -- if (this.canPassThrough(level, this.getFlowing(), pos, state, direction1, blockPos, blockState, fluidState)) { -- if (spreadContext.isHole(blockPos)) { -- return depth; -- } ++ + for (Direction dir : Direction.Plane.HORIZONTAL) { + if (dir == excludedDirection) continue; - -- if (depth < this.getSlopeFindDistance(level)) { -- int slopeDistance = this.getSlopeDistance(level, blockPos, depth + 1, direction1.getOpposite(), blockState, spreadContext); -- if (slopeDistance < i) { -- i = slopeDistance; -- } -- } ++ + BlockPos neighborPos = startPos.relative(dir); + BlockState neighborState = spreadContext.getBlockStateIfLoaded(neighborPos); + if (neighborState == null) continue; @@ -64,10 +40,26 @@ index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..33e5c19362de8b4002c23959661535b8 + queue.add(new FlowingFluid.SlopeDistanceNode(neighborPos, initialDepth, dir.getOpposite(), neighborState)); + } + } -+ + +- for (Direction direction1 : Direction.Plane.HORIZONTAL) { +- if (direction1 != direction) { +- BlockPos blockPos = pos.relative(direction1); +- BlockState blockState = spreadContext.getBlockStateIfLoaded(blockPos); // Paper - Prevent chunk loading from fluid flowing +- if (blockState == null) continue; // Paper - Prevent chunk loading from fluid flowing +- FluidState fluidState = blockState.getFluidState(); +- if (this.canPassThrough(level, this.getFlowing(), pos, state, direction1, blockPos, blockState, fluidState)) { +- if (spreadContext.isHole(blockPos)) { +- return depth; +- } + int slopeFindDistance = this.getSlopeFindDistance(level); + int minDistance = 1000; -+ + +- if (depth < this.getSlopeFindDistance(level)) { +- int slopeDistance = this.getSlopeDistance(level, blockPos, depth + 1, direction1.getOpposite(), blockState, spreadContext); +- if (slopeDistance < i) { +- i = slopeDistance; +- } +- } + // Process the queue + while (!queue.isEmpty()) { + FlowingFluid.SlopeDistanceNode current = queue.poll(); @@ -76,6 +68,7 @@ index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..33e5c19362de8b4002c23959661535b8 + } + + if (current.depth >= slopeFindDistance) continue; ++ + for (Direction dir : Direction.Plane.HORIZONTAL) { + if (dir == current.excludedDir) continue; + @@ -100,7 +93,8 @@ index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..33e5c19362de8b4002c23959661535b8 + } + + private static long encodeSlopeNode(BlockPos pos, Direction excludedDir) { -+ return ((long) pos.getX() & 0xFFFFFFFFL) << 32 | ((long) pos.getZ() & 0xFFFFFFFFL) << 4 | (excludedDir.ordinal() & 0x0F);} ++ return ((long) pos.getX() & 0xFFFFFFFFL) << 32 | ((long) pos.getZ() & 0xFFFFFFFFL) << 4 | (excludedDir.ordinal() & 0x0F); ++ } + + private static class SlopeDistanceNode { + final BlockPos pos; @@ -115,5 +109,7 @@ index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..33e5c19362de8b4002c23959661535b8 + this.state = state; + } } ++ // Leaf end - Use BFS on getSlopeDistance boolean isWaterHole(BlockGetter level, BlockPos pos, BlockState state, BlockPos belowPos, BlockState belowState) { + return canPassThroughWall(Direction.DOWN, level, pos, state, belowPos, belowState) diff --git a/leaf-server/minecraft-patches/features/0152-Async-Block-Finding.patch b/leaf-server/minecraft-patches/features/0152-Async-Block-Finding.patch index 24c4799e..ca8e3fac 100644 --- a/leaf-server/minecraft-patches/features/0152-Async-Block-Finding.patch +++ b/leaf-server/minecraft-patches/features/0152-Async-Block-Finding.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Async Block Finding diff --git a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..135506968893cd164c4d416ce4d356e9f0ed3977 100644 +index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..007da9cb39ff76285c52ce0abdff60997acdff0f 100644 --- a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java +++ b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java @@ -20,6 +20,18 @@ public abstract class MoveToBlockGoal extends Goal { @@ -31,70 +31,62 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..135506968893cd164c4d416ce4d356e9 super.stop(); this.blockPos = BlockPos.ZERO; this.mob.movingTarget = null; -+ // Leaf start - Reset async state on goal stop ++ // Leaf start - Async Block Finding - Reset async state on goal stop + this.candidateBlocks.clear(); + this.asyncSearchInProgress = false; -+ // Leaf end ++ // Leaf end - Async Block Finding - Reset async state on goal stop } // Paper end -@@ -53,23 +69,28 @@ public abstract class MoveToBlockGoal extends Goal { +@@ -53,23 +69,23 @@ public abstract class MoveToBlockGoal extends Goal { } protected int nextStartTick(PathfinderMob creature) { - return reducedTickDelay(200 + creature.getRandom().nextInt(200)); -+ // Use the static method from the Goal class directly -+ return Goal.reducedTickDelay(200 + creature.getRandom().nextInt(200)); ++ return Goal.reducedTickDelay(200 + creature.getRandom().nextInt(200)); // Leaf - Async Block Finding - Use the static method from the Goal class directly } @Override public boolean canContinueToUse() { - return this.tryTicks >= -this.maxStayTicks && this.tryTicks <= 1200 && this.isValidTarget(this.mob.level(), this.blockPos); -+ return this.tryTicks >= -this.maxStayTicks && this.tryTicks <= 1200 && this.blockPos != BlockPos.ZERO && this.isValidTarget(this.mob.level(), this.blockPos); ++ return this.tryTicks >= -this.maxStayTicks && this.tryTicks <= 1200 && this.blockPos != BlockPos.ZERO && this.isValidTarget(this.mob.level(), this.blockPos); // Leaf - Async Block Finding } @Override public void start() { - this.moveMobToBlock(); -+ if (this.blockPos != BlockPos.ZERO) { -+ this.moveMobToBlock(); -+ } ++ if (this.blockPos != BlockPos.ZERO) this.moveMobToBlock(); // Leaf - Async Block Finding this.tryTicks = 0; this.maxStayTicks = this.mob.getRandom().nextInt(this.mob.getRandom().nextInt(1200) + 1200) + 1200; } protected void moveMobToBlock() { - this.mob.getNavigation().moveTo(this.blockPos.getX() + 0.5, this.blockPos.getY() + 1, this.blockPos.getZ() + 0.5, this.speedModifier); -+ if (this.blockPos != BlockPos.ZERO) { -+ this.mob.getNavigation().moveTo(this.blockPos.getX() + 0.5, this.blockPos.getY() + 1, this.blockPos.getZ() + 0.5, this.speedModifier); -+ } ++ if (this.blockPos != BlockPos.ZERO) this.mob.getNavigation().moveTo(this.blockPos.getX() + 0.5, this.blockPos.getY() + 1, this.blockPos.getZ() + 0.5, this.speedModifier); // Leaf - Async Block Finding } public double acceptedDistance() { -@@ -77,7 +98,7 @@ public abstract class MoveToBlockGoal extends Goal { +@@ -77,7 +93,7 @@ public abstract class MoveToBlockGoal extends Goal { } protected BlockPos getMoveToTarget() { - return this.blockPos.above(); -+ return this.blockPos != BlockPos.ZERO ? this.blockPos.above() : BlockPos.ZERO; ++ return this.blockPos != BlockPos.ZERO ? this.blockPos.above() : BlockPos.ZERO; // Leaf - Async Block Finding } @Override -@@ -87,7 +108,13 @@ public abstract class MoveToBlockGoal extends Goal { +@@ -87,7 +103,10 @@ public abstract class MoveToBlockGoal extends Goal { @Override public void tick() { -+ if (this.blockPos == BlockPos.ZERO) { -+ return; -+ } -+ ++ if (this.blockPos == BlockPos.ZERO) return; // Leaf - Async Block Finding BlockPos moveToTarget = this.getMoveToTarget(); -+ if (moveToTarget == BlockPos.ZERO) return; ++ if (moveToTarget == BlockPos.ZERO) return; // Leaf - Async Block Finding + if (!moveToTarget.closerToCenterThan(this.mob.position(), this.acceptedDistance())) { this.reachedTarget = false; this.tryTicks++; -@@ -109,20 +136,90 @@ public abstract class MoveToBlockGoal extends Goal { +@@ -109,20 +128,90 @@ public abstract class MoveToBlockGoal extends Goal { } protected boolean findNearestBlock() { @@ -139,7 +131,6 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..135506968893cd164c4d416ce4d356e9 + } + + return false; -+ // Leaf end - Async Block Finding + } + + private void generateCandidateBlocks(BlockPos center, int searchRange, int verticalRange, int verticalStart) { @@ -168,10 +159,11 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..135506968893cd164c4d416ce4d356e9 + } + + protected boolean findNearestBlockSync() { ++ // Leaf end - Async Block Finding int i = this.searchRange; int i1 = this.verticalSearchRange; - BlockPos blockPos = this.mob.blockPosition(); -+ BlockPos blockPosOrigin = this.mob.blockPosition(); ++ BlockPos blockPosOrigin = this.mob.blockPosition(); // Leaf - Async Block Finding BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); for (int i2 = this.verticalSearchStart; i2 <= i1; i2 = i2 > 0 ? -i2 : 1 - i2) { @@ -179,19 +171,13 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..135506968893cd164c4d416ce4d356e9 for (int i4 = 0; i4 <= i3; i4 = i4 > 0 ? -i4 : 1 - i4) { for (int i5 = i4 < i3 && i4 > -i3 ? i3 : 0; i5 <= i3; i5 = i5 > 0 ? -i5 : 1 - i5) { - mutableBlockPos.setWithOffset(blockPos, i4, i2 - 1, i5); -+ mutableBlockPos.setWithOffset(blockPosOrigin, i4, i2 - 1, i5); ++ mutableBlockPos.setWithOffset(blockPosOrigin, i4, i2 - 1, i5); // Leaf - Async Block Finding if (!this.mob.level().hasChunkAt(mutableBlockPos)) continue; // Gale - Airplane - block goal does not load chunks - if this block isn't loaded, continue if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) { - this.blockPos = mutableBlockPos; - this.mob.movingTarget = mutableBlockPos == BlockPos.ZERO ? null : mutableBlockPos.immutable(); // Paper -+ this.blockPos = mutableBlockPos.immutable(); -+ this.mob.movingTarget = this.blockPos == BlockPos.ZERO ? null : this.blockPos; // Paper ++ this.blockPos = mutableBlockPos.immutable(); // Leaf - Async Block Finding ++ this.mob.movingTarget = this.blockPos == BlockPos.ZERO ? null : this.blockPos; // Paper // Leaf - Async Block Finding return true; } } -@@ -134,4 +231,5 @@ public abstract class MoveToBlockGoal extends Goal { - } - - protected abstract boolean isValidTarget(LevelReader level, BlockPos pos); -+ - } diff --git a/leaf-server/minecraft-patches/features/0156-AsyncTargetFinding.patch b/leaf-server/minecraft-patches/features/0156-Async-Target-Finding.patch similarity index 86% rename from leaf-server/minecraft-patches/features/0156-AsyncTargetFinding.patch rename to leaf-server/minecraft-patches/features/0156-Async-Target-Finding.patch index e4ca7ece..9aaa7f88 100644 --- a/leaf-server/minecraft-patches/features/0156-AsyncTargetFinding.patch +++ b/leaf-server/minecraft-patches/features/0156-Async-Target-Finding.patch @@ -1,44 +1,24 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 29 Mar 2025 13:40:46 +0100 -Subject: [PATCH] AsyncTargetFinding +Subject: [PATCH] Async Target Finding diff --git a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java -index 41ee3cdc45ecc8376a2203ed588bb544ed377294..7fe2b75e8c2718851d68429380ac71203d31a58f 100644 +index 41ee3cdc45ecc8376a2203ed588bb544ed377294..ed731177585051abbf129a48dfe4766265cf5617 100644 --- a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java -@@ -1,6 +1,13 @@ - package net.minecraft.world.entity.ai.goal.target; - - import java.util.EnumSet; -+import java.util.List; -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.ExecutorService; -+import java.util.concurrent.Executors; -+import java.util.concurrent.TimeUnit; -+import java.util.concurrent.atomic.AtomicBoolean; -+import java.util.concurrent.atomic.AtomicReference; - import javax.annotation.Nullable; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.server.level.ServerPlayer; -@@ -10,15 +17,42 @@ import net.minecraft.world.entity.ai.goal.Goal; - import net.minecraft.world.entity.ai.targeting.TargetingConditions; - import net.minecraft.world.entity.player.Player; - import net.minecraft.world.phys.AABB; -+import org.dreeam.leaf.config.modules.async.AsyncTargetFinding; - - public class NearestAttackableTargetGoal extends TargetGoal { - private static final int DEFAULT_RANDOM_INTERVAL = 10; +@@ -16,9 +16,37 @@ public class NearestAttackableTargetGoal extends TargetG protected final Class targetType; protected final int randomInterval; @Nullable - protected LivingEntity target; -+ protected volatile LivingEntity target; ++ protected volatile LivingEntity target; // Leaf - Async Target Finding protected TargetingConditions targetConditions; ++ // Leaf start - Async Target Finding + // Single thread executor to prevent overwhelming the server -+ private static final ExecutorService TARGET_FINDER_EXECUTOR = Executors.newSingleThreadExecutor(r -> { ++ private static final java.util.concurrent.ExecutorService TARGET_FINDER_EXECUTOR = java.util.concurrent.Executors.newSingleThreadExecutor(r -> { + Thread thread = new Thread(r, "Leaf - Target-Finder-Thread"); + thread.setDaemon(true); + thread.setPriority(Thread.MIN_PRIORITY); // Lower priority to avoid competing with main thread @@ -46,13 +26,13 @@ index 41ee3cdc45ecc8376a2203ed588bb544ed377294..7fe2b75e8c2718851d68429380ac7120 + }); + + // Flag to track if a search is in progress -+ private final AtomicBoolean isSearching = new AtomicBoolean(false); -+ private final AtomicReference pendingTarget = new AtomicReference<>(null); ++ private final java.util.concurrent.atomic.AtomicBoolean isSearching = new java.util.concurrent.atomic.AtomicBoolean(false); ++ private final java.util.concurrent.atomic.AtomicReference pendingTarget = new java.util.concurrent.atomic.AtomicReference<>(null); + static { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + TARGET_FINDER_EXECUTOR.shutdown(); -+ TARGET_FINDER_EXECUTOR.awaitTermination(2, TimeUnit.SECONDS); ++ TARGET_FINDER_EXECUTOR.awaitTermination(2, java.util.concurrent.TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { @@ -62,29 +42,33 @@ index 41ee3cdc45ecc8376a2203ed588bb544ed377294..7fe2b75e8c2718851d68429380ac7120 + } + })); + } ++ // Leaf end - Async Target Finding + public NearestAttackableTargetGoal(Mob mob, Class targetType, boolean mustSee) { this(mob, targetType, 10, mustSee, false, null); } -@@ -46,8 +80,12 @@ public class NearestAttackableTargetGoal extends TargetG +@@ -46,8 +74,14 @@ public class NearestAttackableTargetGoal extends TargetG if (this.randomInterval > 0 && this.mob.getRandom().nextInt(this.randomInterval) != 0) { return false; } else { - this.findTarget(); - return this.target != null; ++ // Leaf start - Async Target Finding + findTarget(); + LivingEntity pending = pendingTarget.getAndSet(null); + if (pending != null && !pending.isRemoved() && pending.isAlive()) { + this.target = pending; + } + return this.target != null && this.target.isAlive() && !this.target.isRemoved(); ++ // Leaf end - Async Target Finding } } -@@ -55,25 +93,235 @@ public class NearestAttackableTargetGoal extends TargetG +@@ -55,25 +89,239 @@ public class NearestAttackableTargetGoal extends TargetG return this.mob.getBoundingBox().inflate(targetDistance, targetDistance, targetDistance); } ++ // Leaf start - Async Target Finding + // Async find target implementation with safer entity handling protected void findTarget() { - ServerLevel serverLevel = getServerLevel(this.mob); @@ -100,10 +84,10 @@ index 41ee3cdc45ecc8376a2203ed588bb544ed377294..7fe2b75e8c2718851d68429380ac7120 - } else { - this.target = serverLevel.getNearestPlayer(this.getTargetConditions(), this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ()); + // If async is disabled or we're already searching, use sync method -+ if (!AsyncTargetFinding.enabled || !isSearching.compareAndSet(false, true)) { ++ if (!org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled || !isSearching.compareAndSet(false, true)) { + findTargetSync(); + return; -+ } + } + + // Capture mutable state to avoid race conditions + final Mob mob = this.mob; @@ -122,7 +106,7 @@ index 41ee3cdc45ecc8376a2203ed588bb544ed377294..7fe2b75e8c2718851d68429380ac7120 + final Class targetType = this.targetType; + + // Start async search with immutable captured state - using submit instead of runAsync -+ CompletableFuture.supplyAsync(() -> { ++ java.util.concurrent.CompletableFuture.supplyAsync(() -> { + try { + ServerLevel serverLevel = getServerLevel(mob); + if (serverLevel == null) { @@ -139,7 +123,7 @@ index 41ee3cdc45ecc8376a2203ed588bb544ed377294..7fe2b75e8c2718851d68429380ac7120 + x + followDistance, y + followDistance, z + followDistance + ); + -+ List entities = null; ++ java.util.List entities = null; + try { + entities = mob.level().getEntitiesOfClass(targetType, searchArea, entity -> true); + } catch (Exception e) { @@ -169,11 +153,11 @@ index 41ee3cdc45ecc8376a2203ed588bb544ed377294..7fe2b75e8c2718851d68429380ac7120 + pendingTarget.set(result); + } + }); -+ } -+ + } + + @Nullable + private LivingEntity findNearestEntitySafely( -+ List entities, ++ java.util.List entities, + TargetingConditions conditions, + Mob source, + double x, @@ -236,7 +220,7 @@ index 41ee3cdc45ecc8376a2203ed588bb544ed377294..7fe2b75e8c2718851d68429380ac7120 + } + + try { -+ List players = level.players(); ++ java.util.List players = level.players(); + if (players == null || players.isEmpty()) { + return null; + } @@ -310,12 +294,14 @@ index 41ee3cdc45ecc8376a2203ed588bb544ed377294..7fe2b75e8c2718851d68429380ac7120 + } catch (Exception e) { + System.err.println("Error in findTargetSync: " + e.getMessage()); + this.target = null; - } - } - ++ } ++ } ++ // Leaf end - Async Target Finding ++ @Override public void start() { - this.mob.setTarget(this.target, this.target instanceof ServerPlayer ? org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER : org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY, true); // CraftBukkit - reason ++ // Leaf start - Async Target Finding + LivingEntity targetEntity = this.target; + if (targetEntity != null && !targetEntity.isRemoved() && targetEntity.isAlive()) { + try { @@ -327,6 +313,7 @@ index 41ee3cdc45ecc8376a2203ed588bb544ed377294..7fe2b75e8c2718851d68429380ac7120 + this.target = null; + } + } ++ // Leaf end - Async Target Finding super.start(); } diff --git a/leaf-server/minecraft-patches/features/0157-Null-handling-on-MultifaceSpreader.patch b/leaf-server/minecraft-patches/features/0157-Null-handling-on-MultifaceSpreader.patch index 5ca9d329..d31e6565 100644 --- a/leaf-server/minecraft-patches/features/0157-Null-handling-on-MultifaceSpreader.patch +++ b/leaf-server/minecraft-patches/features/0157-Null-handling-on-MultifaceSpreader.patch @@ -6,27 +6,20 @@ Subject: [PATCH] Null handling on MultifaceSpreader WHYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY diff --git a/net/minecraft/world/level/block/MultifaceSpreader.java b/net/minecraft/world/level/block/MultifaceSpreader.java -index 60f47334bb855d5216f57f888f131ba41f728d21..7d54362767ec2acfcb9f8f8703ec706a1c2fd03b 100644 +index 60f47334bb855d5216f57f888f131ba41f728d21..718223a54da75d000e3c3090acf6e28b4283820a 100644 --- a/net/minecraft/world/level/block/MultifaceSpreader.java +++ b/net/minecraft/world/level/block/MultifaceSpreader.java -@@ -10,6 +10,8 @@ import net.minecraft.world.level.BlockGetter; - import net.minecraft.world.level.LevelAccessor; - import net.minecraft.world.level.block.state.BlockState; - -+import static org.dreeam.leaf.config.LeafConfig.LOGGER; -+ - public class MultifaceSpreader { - public static final MultifaceSpreader.SpreadType[] DEFAULT_SPREAD_ORDER = new MultifaceSpreader.SpreadType[]{ - MultifaceSpreader.SpreadType.SAME_POSITION, MultifaceSpreader.SpreadType.SAME_PLANE, MultifaceSpreader.SpreadType.WRAP_AROUND -@@ -148,6 +150,12 @@ public class MultifaceSpreader { +@@ -148,6 +148,14 @@ public class MultifaceSpreader { } default boolean placeBlock(LevelAccessor level, MultifaceSpreader.SpreadPos pos, BlockState state, boolean markForPostprocessing) { ++ // Leaf start - Null handling on MultifaceSpreader + // Check for null + if (pos.source() == null || pos.pos() == null) { -+ LOGGER.warn("Invalid SpreadPos with null source or position: {}", pos); ++ org.dreeam.leaf.config.LeafConfig.LOGGER.warn("Invalid SpreadPos with null source or position: {}", pos); + return false; + } ++ // Leaf end - Null handling on MultifaceSpreader + BlockState stateForPlacement = this.getStateForPlacement(state, level, pos.pos(), pos.face()); if (stateForPlacement != null) { diff --git a/leaf-server/minecraft-patches/features/0158-More-virtual-threads.patch b/leaf-server/minecraft-patches/features/0158-More-virtual-threads.patch index b3a142b6..74803bd2 100644 --- a/leaf-server/minecraft-patches/features/0158-More-virtual-threads.patch +++ b/leaf-server/minecraft-patches/features/0158-More-virtual-threads.patch @@ -5,50 +5,34 @@ Subject: [PATCH] More virtual threads diff --git a/net/minecraft/Util.java b/net/minecraft/Util.java -index b097f685e826e70008e3a096ee5f1d4fccf25680..f374428c65e929b8795f2a0ddfe3137b4afca9bc 100644 +index b097f685e826e70008e3a096ee5f1d4fccf25680..aa79e95dc93927ce224c02e4382c7246cb48d51c 100644 --- a/net/minecraft/Util.java +++ b/net/minecraft/Util.java -@@ -61,6 +61,7 @@ import java.util.concurrent.ForkJoinPool; - import java.util.concurrent.ForkJoinWorkerThread; - import java.util.concurrent.LinkedBlockingQueue; - import java.util.concurrent.TimeUnit; -+import java.util.concurrent.ThreadFactory; - import java.util.concurrent.atomic.AtomicInteger; - import java.util.function.BiFunction; - import java.util.function.BooleanSupplier; -@@ -86,6 +87,10 @@ import net.minecraft.util.TimeSource; - import net.minecraft.util.datafix.DataFixers; - import net.minecraft.world.level.block.state.properties.Property; - import org.slf4j.Logger; -+import org.galemc.gale.virtualthread.VirtualThreadService; // Gale - virtual thread support -+import org.dreeam.leaf.config.modules.opt.VT4DownloadPool; -+import org.dreeam.leaf.config.modules.opt.VT4ProfileExecutor; -+ - - public class Util { - static final Logger LOGGER = LogUtils.getLogger(); -@@ -97,7 +102,7 @@ public class Util { +@@ -97,7 +97,8 @@ public class Util { public static final TracingExecutor DIMENSION_DATA_IO_POOL = makeExtraIoExecutor("Dimension-Data-IO-Worker-"); // Paper - Separate dimension data IO pool private static final TracingExecutor DOWNLOAD_POOL = makeIoExecutor("Download-", true); // 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() { ++ // Leaf start - More virtual threads + public static final ExecutorService PROFILE_EXECUTOR = createProfileExecutor(); /* new Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { private final AtomicInteger count = new AtomicInteger(); -@@ -110,7 +115,27 @@ public class Util { +@@ -110,7 +111,30 @@ public class Util { }); return ret; } - }); + }); */ ++ + private static ExecutorService createProfileExecutor() { -+ final ThreadFactory factory; -+ if (VT4ProfileExecutor.enabled && VirtualThreadService.isSupported()) { -+ factory = VirtualThreadService.get().createFactory(); ++ final java.util.concurrent.ThreadFactory factory; ++ if (org.dreeam.leaf.config.modules.opt.VT4ProfileExecutor.enabled && org.galemc.gale.virtualthread.VirtualThreadService.isSupported()) { ++ factory = org.galemc.gale.virtualthread.VirtualThreadService.get().createFactory(); + } else { -+ factory = new ThreadFactory() { ++ factory = new java.util.concurrent.ThreadFactory() { + private final AtomicInteger count = new AtomicInteger(); ++ + @Override + public Thread newThread(Runnable run) { + Thread ret = new Thread(run); @@ -62,10 +46,11 @@ index b097f685e826e70008e3a096ee5f1d4fccf25680..f374428c65e929b8795f2a0ddfe3137b + } + return Executors.newFixedThreadPool(2, factory); + } ++ // Leaf end - More virtual threads // Paper end - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread private static final DateTimeFormatter FILENAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT); public static final int LINEAR_LOOKUP_THRESHOLD = 8; -@@ -254,16 +279,29 @@ public class Util { +@@ -254,16 +278,31 @@ public class Util { } private static TracingExecutor makeIoExecutor(String name, boolean daemon) { @@ -79,16 +64,17 @@ index b097f685e826e70008e3a096ee5f1d4fccf25680..f374428c65e929b8795f2a0ddfe3137b - thread.setUncaughtExceptionHandler(Util::onThreadException); - return thread; - })); -+ final ThreadFactory factory; ++ // Leaf start - More virtual threads ++ final java.util.concurrent.ThreadFactory factory; + final boolean useVirtualThreads; // Gale - virtual thread support + if (name.startsWith("Download-")) { // Gale - virtual thread support -+ useVirtualThreads = VT4DownloadPool.enabled && VirtualThreadService.isSupported(); // Gale - virtual thread support ++ useVirtualThreads = org.dreeam.leaf.config.modules.opt.VT4DownloadPool.enabled && org.galemc.gale.virtualthread.VirtualThreadService.isSupported(); // Gale - virtual thread support + } else { + useVirtualThreads = false; + } + + if (useVirtualThreads) { -+ factory = VirtualThreadService.get().createFactory(); ++ factory = org.galemc.gale.virtualthread.VirtualThreadService.get().createFactory(); + } else { + AtomicInteger atomicInteger = new AtomicInteger(1); + factory = runnable -> { @@ -102,10 +88,11 @@ index b097f685e826e70008e3a096ee5f1d4fccf25680..f374428c65e929b8795f2a0ddfe3137b + }; + } + return new TracingExecutor(Executors.newCachedThreadPool(factory)); ++ // Leaf end - More virtual threads } // Paper start - Separate dimension data IO pool -@@ -1099,7 +1137,7 @@ public class Util { +@@ -1099,7 +1138,7 @@ public class Util { } public static Typed readTypedOrThrow(Type type, Dynamic data, boolean partial) { @@ -114,26 +101,12 @@ index b097f685e826e70008e3a096ee5f1d4fccf25680..f374428c65e929b8795f2a0ddfe3137b try { return partial ? dataResult.getPartialOrThrow(IllegalStateException::new) : dataResult.getOrThrow(IllegalStateException::new); -@@ -1143,12 +1181,14 @@ public class Util { - - private final String telemetryName; - -+ // Paper start - Fix warnings on build by removing client-only code - OS(final String telemetryName) { - this.telemetryName = telemetryName; +@@ -1148,7 +1187,7 @@ public class Util { } public void openUri(URI uri) { - throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code -+ // This method is not useful on dedicated servers. -+ // throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code ++ // throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code // Leaf - More virtual threads - This method is not useful on dedicated servers } public void openFile(File file) { -@@ -1179,5 +1219,6 @@ public class Util { - public String telemetryName() { - return this.telemetryName; - } -+ // Paper end - Fix warnings on build by removing client-only code - } - } diff --git a/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch b/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch index c95ab0c5..8eae877f 100644 --- a/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch +++ b/leaf-server/paper-patches/features/0033-Async-playerdata-saving.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Async playerdata saving diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java -index f2d87c12dd19210ce7e2147fada5c10191008632..bd6608787abcd1869f66d67297c6b4252193080a 100644 +index f2d87c12dd19210ce7e2147fada5c10191008632..14da4c731391f69fef104b6b3b7f2f977fe5ee95 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java @@ -207,7 +207,7 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa @@ -17,7 +17,7 @@ index f2d87c12dd19210ce7e2147fada5c10191008632..bd6608787abcd1869f66d67297c6b425 } private CompoundTag getBukkitData() { -@@ -813,16 +813,9 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa +@@ -813,16 +813,7 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa * @param compoundTag */ private void save(CompoundTag compoundTag) { @@ -31,9 +31,7 @@ index f2d87c12dd19210ce7e2147fada5c10191008632..bd6608787abcd1869f66d67297c6b425 - } catch (java.io.IOException e) { - e.printStackTrace(); - } -+ // Leaf start - Async playerdata saving -+ server.console.playerDataStorage.save(this.getName(), this.getUniqueId(), this.getUniqueId().toString(), compoundTag); -+ // Leaf end ++ server.console.playerDataStorage.save(this.getName(), this.getUniqueId(), this.getUniqueId().toString(), compoundTag); // Leaf - Async playerdata saving } // Purpur end - OfflinePlayer API } diff --git a/scripts/prepareRelease.sh b/scripts/prepareRelease.sh index 39a70ce5..ef117aef 100755 --- a/scripts/prepareRelease.sh +++ b/scripts/prepareRelease.sh @@ -3,6 +3,7 @@ set -e IS_EOL=false IS_UNSUPPORTED=false +IS_DEV=false JAR_NAME="leaf-1.21.4" CURRENT_TAG="ver-1.21.4" @@ -89,6 +90,16 @@ if [ $IS_UNSUPPORTED = true ]; then } >> $RELEASE_NOTES fi +# Dev build warning +if [ $IS_DEV = true ]; then + { + echo "" + echo "> [!WARNING]" + echo "> This is the early dev build, only for testing usage." + echo "> Do not use in the production environment!" + } >> $RELEASE_NOTES +fi + # Delete current release tag if git show-ref --tags $CURRENT_TAG --quiet; then { From 7ea4d4b639dfb2f742cd4e0af9280b733a1158f8 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Thu, 17 Apr 2025 03:04:03 -0400 Subject: [PATCH 26/27] [ci skip] cleanup --- .../features/0086-Nitori-Async-playerdata-saving.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch b/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch index 56763f6c..3ed4597e 100644 --- a/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch +++ b/leaf-server/minecraft-patches/features/0086-Nitori-Async-playerdata-saving.patch @@ -61,7 +61,7 @@ index de43e54698125ce9f319d4889dd49f7029fe95e0..742bd4b60321adc9e63c3de910ea95f4 public Optional getIconFile() { diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java -index c44110b123ba5912af18faf0065e9ded780da9b7..bf5a697c602427768a164a8ab8af1074688d81fd 100644 +index c44110b123ba5912af18faf0065e9ded780da9b7..fd8b4832c8b4a52bd8f9b3ea59111af85127b573 100644 --- a/net/minecraft/world/level/storage/PlayerDataStorage.java +++ b/net/minecraft/world/level/storage/PlayerDataStorage.java @@ -25,6 +25,7 @@ public class PlayerDataStorage { @@ -159,7 +159,7 @@ index c44110b123ba5912af18faf0065e9ded780da9b7..bf5a697c602427768a164a8ab8af1074 + } + } + } -+ // Leaf end ++ // Leaf end - Async playerdata saving + private void backup(String name, String stringUuid, String suffix) { // CraftBukkit Path path = this.playerDir.toPath(); From dd22d9cafffece7d469a7d702f63952abea441d2 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Thu, 17 Apr 2025 03:17:40 -0400 Subject: [PATCH 27/27] [ci skip] cleanup imports --- .../java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java | 6 +++++- .../dreeam/leaf/config/modules/async/AsyncBlockFinding.java | 1 - .../dreeam/leaf/config/modules/async/AsyncChunkSend.java | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java b/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java index 0bdc32db..40f552d1 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java @@ -3,7 +3,11 @@ package org.dreeam.leaf.async; import org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave; import java.util.Optional; -import java.util.concurrent.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; public class AsyncPlayerDataSaving { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncBlockFinding.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncBlockFinding.java index 0a3715fc..fffd4b3e 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncBlockFinding.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncBlockFinding.java @@ -2,7 +2,6 @@ package org.dreeam.leaf.config.modules.async; import org.dreeam.leaf.config.ConfigModules; import org.dreeam.leaf.config.EnumConfigCategory; -import org.dreeam.leaf.config.annotations.Experimental; public class AsyncBlockFinding extends ConfigModules { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncChunkSend.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncChunkSend.java index 2e2dae90..34fae012 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncChunkSend.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncChunkSend.java @@ -2,7 +2,6 @@ package org.dreeam.leaf.config.modules.async; import org.dreeam.leaf.config.ConfigModules; import org.dreeam.leaf.config.EnumConfigCategory; -import org.dreeam.leaf.config.annotations.Experimental; public class AsyncChunkSend extends ConfigModules {