9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-19 15:09:25 +00:00
This commit is contained in:
Dreeam
2025-04-17 02:45:56 -04:00
parent 502701329c
commit dff5b11bd9
8 changed files with 147 additions and 226 deletions

View File

@@ -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<AdvancementHolder, AdvancementProgress> progress = new LinkedHashMap<>();
private final Set<AdvancementHolder> visible = new HashSet<>();
- private final Set<AdvancementHolder> progressChanged = new HashSet<>();
- private final Set<AdvancementNode> rootsToUpdate = new HashSet<>();
+ // Leaf start - SparklyPaper - parallel world ticking
+ private final Set<AdvancementHolder> progressChanged = new HashSet<>(); // Used when PWT is disabled
+ private final Set<AdvancementHolder> progressChangedConcurrent = ConcurrentHashMap.newKeySet(); // Used when PWT is enabled
+ private final Set<AdvancementHolder> progressChangedConcurrent = java.util.concurrent.ConcurrentHashMap.newKeySet(); // Used when PWT is enabled
+ private final Set<AdvancementNode> 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<AdvancementHolder> relevantProgressSet = useConcurrent ? this.progressChangedConcurrent : this.progressChanged;
+ if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !relevantProgressSet.isEmpty()) {
Map<ResourceLocation, AdvancementProgress> map = new HashMap<>();
Set<AdvancementHolder> set = new java.util.TreeSet<>(java.util.Comparator.comparing(adv -> adv.id().toString())); // Paper - Changed from HashSet to TreeSet ordered alphabetically.
Set<ResourceLocation> 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

View File

@@ -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<FlowingFluid.SlopeDistanceNode> 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)

View File

@@ -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);
+
}

View File

@@ -1,44 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
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<T extends LivingEntity> extends TargetGoal {
private static final int DEFAULT_RANDOM_INTERVAL = 10;
@@ -16,9 +16,37 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
protected final Class<T> 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<LivingEntity> 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<LivingEntity> 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<T> targetType, boolean mustSee) {
this(mob, targetType, 10, mustSee, false, null);
}
@@ -46,8 +80,12 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
@@ -46,8 +74,14 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> 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<T extends LivingEntity> extends TargetG
@@ -55,25 +89,239 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> 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<T> 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<T> entities = null;
+ java.util.List<T> 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<? extends LivingEntity> entities,
+ java.util.List<? extends LivingEntity> entities,
+ TargetingConditions conditions,
+ Mob source,
+ double x,
@@ -236,7 +220,7 @@ index 41ee3cdc45ecc8376a2203ed588bb544ed377294..7fe2b75e8c2718851d68429380ac7120
+ }
+
+ try {
+ List<? extends Player> players = level.players();
+ java.util.List<? extends Player> 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();
}

View File

@@ -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) {

View File

@@ -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 <T> Typed<T> readTypedOrThrow(Type<T> 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
}
}

View File

@@ -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
}

View File

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