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] 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 {