2026-07-09 00:03:09 +08:00
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Wed, 8 Jul 2026 21:12:31 +0800
Subject: [PATCH] Fix region threading with entity ai data access
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
2026-07-14 15:09:17 +08:00
index 3b1c3e84ab24b6e3a0e4d129b3617b393b6d6651..f421702fef63846bffaf55061c3f9c1bb1248bfa 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -299,6 +299,11 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
if (Objects.equals(currentTarget, target)) {
return false;
}
2026-07-14 11:32:51 +08:00
+ // Shiroha - Fix region threading with entity ai data access
2026-07-09 00:03:09 +08:00
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(target)) {
+ return false;
+ }
2026-07-14 11:32:51 +08:00
+ // Shiroha end
2026-07-09 00:03:09 +08:00
LivingEntity originalTarget = target;
target = asValidTarget(target);
if (reason != null) {
diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java
2026-07-14 15:09:17 +08:00
index e097bc1268b4050e3948413c52dea69950858458..16314f63fdf6f81819b2b660c48d0cf3025aa8c1 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/ai/Brain.java
+++ b/net/minecraft/world/entity/ai/Brain.java
@@ -383,7 +383,7 @@ public class Brain<E extends LivingEntity> {
}
public void tick(final ServerLevel level, final E body) {
- this.forgetOutdatedMemories();
2026-07-14 11:32:51 +08:00
+ this.forgetOutdatedMemories(body); // Shiroha - Fix region threading with entity ai data access
2026-07-09 00:03:09 +08:00
this.tickSensors(level, body);
this.startEachNonRunningBehavior(level, body);
this.tickEachRunningBehavior(level, body);
@@ -395,8 +395,8 @@ public class Brain<E extends LivingEntity> {
}
}
- private void forgetOutdatedMemories() {
- this.memories.values().forEach(MemorySlot::tick);
2026-07-14 11:32:51 +08:00
+ private void forgetOutdatedMemories(E body) { // Shiroha - Fix region threading with entity ai data access
+ this.memories.values().forEach(slot -> slot.tick(body)); // Shiroha - Fix region threading with entity ai data access
2026-07-09 00:03:09 +08:00
}
public void stopAll(final ServerLevel level, final E body) {
diff --git a/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java b/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java
2026-07-14 15:09:17 +08:00
index f744ecd20dc70786311a7162b52aa4ceca0717db..231701ff41b519106e33011f555ee2c476085445 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java
+++ b/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java
@@ -81,6 +81,11 @@ public class BehaviorUtils {
public static void setWalkAndLookTargetMemories(
final LivingEntity walker, final PositionTracker target, final float speedModifier, final int closeEnoughDistance
) {
2026-07-14 11:32:51 +08:00
+ // Shiroha - Fix region threading with entity ai data access
2026-07-09 00:03:09 +08:00
+ if (!target.checkThread(walker.level())) {
+ return;
+ }
2026-07-14 11:32:51 +08:00
+ // Shiroha end
2026-07-09 00:03:09 +08:00
WalkTarget walkTarget = new WalkTarget(target, speedModifier, closeEnoughDistance);
walker.getBrain().setMemory(MemoryModuleType.LOOK_TARGET, target);
walker.getBrain().setMemory(MemoryModuleType.WALK_TARGET, walkTarget);
diff --git a/net/minecraft/world/entity/ai/behavior/BlockPosTracker.java b/net/minecraft/world/entity/ai/behavior/BlockPosTracker.java
2026-07-14 15:09:17 +08:00
index 68251875edfa47ac34c64f99510998ad4bbb14b4..5eb0c950944b45b0878c9a8d60432712377eb895 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/ai/behavior/BlockPosTracker.java
+++ b/net/minecraft/world/entity/ai/behavior/BlockPosTracker.java
@@ -37,4 +37,11 @@ public class BlockPosTracker implements PositionTracker {
public String toString() {
return "BlockPosTracker{blockPos=" + this.blockPos + ", centerPosition=" + this.centerPosition + "}";
}
+
2026-07-14 11:32:51 +08:00
+ // Shiroha start - Fix region threading with entity ai data access
2026-07-09 00:03:09 +08:00
+ @Override
+ public boolean checkThread(net.minecraft.world.level.Level currOwnedByLevel) {
+ return ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(currOwnedByLevel, this.blockPos);
+ }
2026-07-14 11:32:51 +08:00
+ // Shiroha end
2026-07-09 00:03:09 +08:00
}
diff --git a/net/minecraft/world/entity/ai/behavior/EntityTracker.java b/net/minecraft/world/entity/ai/behavior/EntityTracker.java
2026-07-14 15:09:17 +08:00
index 3ac52b025ac3e1a3f9135b8e593a385a847afe0f..d2c68c78d0a2f334169a92171081eeb4363399e8 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/ai/behavior/EntityTracker.java
+++ b/net/minecraft/world/entity/ai/behavior/EntityTracker.java
@@ -55,4 +55,11 @@ public class EntityTracker implements PositionTracker {
public String toString() {
return "EntityTracker for " + this.entity;
}
+
2026-07-14 11:32:51 +08:00
+ // Shiroha start - Fix region threading with entity ai data access
2026-07-09 00:03:09 +08:00
+ @Override
+ public boolean checkThread(net.minecraft.world.level.Level currOwnedByLevel) {
+ return ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.entity);
+ }
2026-07-14 11:32:51 +08:00
+ // Shiroha end
2026-07-09 00:03:09 +08:00
}
diff --git a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
2026-08-15 01:58:11 +08:00
index b40da004b5281a041ee896ae176bfb9c4660f353..32df97c0e5e886d2c7ab98506c8a950d4034b472 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
+++ b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
2026-08-15 01:58:11 +08:00
@@ -52,7 +52,7 @@ public class MoveToTargetSink extends Behavior<Mob> {
Brain<?> brain = body.getBrain();
WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
boolean reachedTarget = this.reachedTarget(body, walkTarget);
- if (!reachedTarget && this.tryComputePath(body, walkTarget, level.getGameTime())) {
+ if (!reachedTarget && walkTarget.getTarget().checkThread(body.level()) && this.tryComputePath(body, walkTarget, level.getGameTime())) { // Shiroha - Fix region threading with entity ai data access
this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
return true;
}
@@ -108,7 +108,7 @@ public class MoveToTargetSink extends Behavior<Mob> {
2026-07-09 00:03:09 +08:00
2026-08-15 01:58:11 +08:00
if (newPath != null && this.lastTargetPos != null) {
WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
- if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0 && this.tryComputePath(body, walkTarget, level.getGameTime())) {
+ if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0 && walkTarget.getTarget().checkThread(body.level()) && this.tryComputePath(body, walkTarget, level.getGameTime())) { // Shiroha - Fix region threading with entity ai data access
this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
this.start(level, body, timestamp);
}
@@ -118,7 +118,6 @@ public class MoveToTargetSink extends Behavior<Mob> {
2026-07-09 00:03:09 +08:00
private boolean tryComputePath(final Mob body, final WalkTarget walkTarget, final long timestamp) {
BlockPos targetPos = walkTarget.getTarget().currentBlockPosition();
this.path = body.getNavigation().createPath(targetPos, 0);
2026-08-15 01:58:11 +08:00
- this.speedModifier = walkTarget.getSpeedModifier();
2026-07-09 00:03:09 +08:00
Brain<?> brain = body.getBrain();
if (this.reachedTarget(body, walkTarget)) {
2026-08-15 01:58:11 +08:00
brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
2026-07-09 00:03:09 +08:00
diff --git a/net/minecraft/world/entity/ai/behavior/PositionTracker.java b/net/minecraft/world/entity/ai/behavior/PositionTracker.java
2026-07-14 15:09:17 +08:00
index ce6cf5ecfb190428e3ef9b7dd39c98e3d27a7b9d..3eea48aad910760683e30594a9c1851aa17ce88c 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/ai/behavior/PositionTracker.java
+++ b/net/minecraft/world/entity/ai/behavior/PositionTracker.java
@@ -10,4 +10,6 @@ public interface PositionTracker {
BlockPos currentBlockPosition();
boolean isVisibleBy(final LivingEntity body);
+
2026-07-14 11:32:51 +08:00
+ boolean checkThread(net.minecraft.world.level.Level currOwnedByLevel); // Shiroha - Fix region threading with entity ai data access
2026-07-09 00:03:09 +08:00
}
diff --git a/net/minecraft/world/entity/ai/behavior/SleepInBed.java b/net/minecraft/world/entity/ai/behavior/SleepInBed.java
2026-07-14 15:09:17 +08:00
index 16017d819c28b077f732e2ef571eac179d24e323..2e248e4fcdec5830d83ecabf3df163117d2612c9 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/ai/behavior/SleepInBed.java
+++ b/net/minecraft/world/entity/ai/behavior/SleepInBed.java
@@ -49,6 +49,11 @@ public class SleepInBed extends Behavior<LivingEntity> {
return false;
}
2026-07-14 11:32:51 +08:00
+ // Shiroha - Fix region threading with entity ai data access
2026-07-09 00:03:09 +08:00
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(level, target.pos())) {
+ return false;
+ }
2026-07-14 11:32:51 +08:00
+ // Shiroha end
2026-07-09 00:03:09 +08:00
Optional<Long> lastWokenMemory = brain.getMemory(MemoryModuleType.LAST_WOKEN);
if (lastWokenMemory.isPresent()) {
long timeSinceLastWoken = level.getGameTime() - lastWokenMemory.get();
@@ -56,7 +61,6 @@ public class SleepInBed extends Behavior<LivingEntity> {
return false;
}
}
-
BlockState blockState = level.getBlockStateIfLoaded(target.pos()); // Paper - Prevent sync chunk loads when villagers try to find beds
if (blockState == null) return false; // Paper - Prevent sync chunk loads when villagers try to find beds
return target.pos().closerToCenterThan(body.position(), 2.0) && blockState.is(BlockTags.BEDS) && !blockState.getValue(BedBlock.OCCUPIED);
diff --git a/net/minecraft/world/entity/ai/memory/MemorySlot.java b/net/minecraft/world/entity/ai/memory/MemorySlot.java
2026-07-14 15:09:17 +08:00
index 88a89b4c72cc99dc89d3f3cc928b2dfda4125759..b4bdc7eff97b058ee8ae2c9e4321b53c824478c5 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/ai/memory/MemorySlot.java
+++ b/net/minecraft/world/entity/ai/memory/MemorySlot.java
@@ -13,7 +13,7 @@ public class MemorySlot<T> {
this.timeToLive = timeToLive;
}
- public void tick() {
2026-07-14 11:32:51 +08:00
+ public void tick(net.minecraft.world.entity.Entity owner) { // Shiroha - Fix region threading with entity ai data access
2026-07-09 00:03:09 +08:00
if (this.hasValue() && this.canExpire()) {
if (this.hasExpired()) {
this.clear();
@@ -21,6 +21,41 @@ public class MemorySlot<T> {
this.timeToLive--;
}
}
2026-07-14 11:32:51 +08:00
+ // Shiroha start - Fix region threading with entity ai data access
2026-07-09 00:03:09 +08:00
+ final net.minecraft.world.level.Level ownerLevel = owner.level();
+
+ // type: entity
2026-07-14 12:22:17 +08:00
+ if (io.nanachiyo0721.shiroha.config.modules.fixes.ForceCleanupEntityBrainMemoryConfig.enabledForEntity && this.value instanceof net.minecraft.world.entity.Entity entity) {
2026-07-09 00:03:09 +08:00
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(entity)) {
+ this.clear();
+ }
+ }
+
+ // type: block_pos
2026-07-14 12:22:17 +08:00
+ if (io.nanachiyo0721.shiroha.config.modules.fixes.ForceCleanupEntityBrainMemoryConfig.enabledForBlockPos && this.value instanceof net.minecraft.core.BlockPos blockPos) {
2026-07-09 00:03:09 +08:00
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(ownerLevel, blockPos)) {
+ this.clear();
+ }
+ }
+
+
+ //type: position_tracker and walk_target
2026-07-14 12:22:17 +08:00
+ if (io.nanachiyo0721.shiroha.config.modules.fixes.ForceCleanupEntityBrainMemoryConfig.enabledForPositionTracker) {
2026-07-09 00:03:09 +08:00
+ net.minecraft.world.entity.ai.behavior.PositionTracker tracker = null;
+
+ if (value instanceof net.minecraft.world.entity.ai.behavior.PositionTracker positionTracker) {
+ tracker = positionTracker;
+ }
+
+ if (value instanceof net.minecraft.world.entity.ai.memory.WalkTarget walkTarget) {
+ tracker = walkTarget.getTarget();
+ }
+
+ if (tracker != null && !tracker.checkThread(owner.level())) {
+ this.clear();
+ }
+ }
2026-07-14 11:32:51 +08:00
+ // Shiroha end
2026-07-09 00:03:09 +08:00
}
public static <T> MemorySlot<T> create() {
diff --git a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
2026-08-15 01:58:11 +08:00
index e44814cfb6afb594456b8215bd13a92e93de2c85..d4b65f13c137495d436b7e8133e0ce09ee2e08c0 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
@@ -60,6 +60,17 @@ public class FlyingPathNavigation extends PathNavigation {
if (!this.isDone()) {
Vec3 target = this.path.getNextEntityPos(this.mob);
2026-08-15 01:58:11 +08:00
+ // Shiroha start - Fix region threading with entity ai data access
2026-07-14 12:22:17 +08:00
+ if (io.nanachiyo0721.shiroha.config.modules.fixes.PathfindingFixesConfig.breakDownPathfindingWhenOutOfRegion) {
2026-07-09 00:03:09 +08:00
+ // we assume that:
+ // 1. The code above doesn't touch the 'main thread context' with the position from 'this.path'
+ // 2. The pathfinder could correctly recompute or discard the incorrect target position and this situation is happening rarely
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.mob.level(), target)) {
+ this.hasDelayedRecomputation = true;
+ return;
+ }
+ }
2026-07-14 11:32:51 +08:00
+ // Shiroha end
2026-07-09 00:03:09 +08:00
this.mob.getMoveControl().setWantedPosition(target.x, target.y, target.z, this.speedModifier);
}
}
diff --git a/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/net/minecraft/world/entity/ai/navigation/PathNavigation.java
2026-08-15 01:58:11 +08:00
index 2ea1ec39a37899ae1510d0036aa670e758077537..3acafc2aa1219efb0d7a591b91afa42640bcd584 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/ai/navigation/PathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/PathNavigation.java
@@ -188,6 +188,18 @@ public abstract class PathNavigation {
}
}
// Paper end - EntityPathfindEvent
2026-07-14 11:32:51 +08:00
+ // Shiroha start - Fix region threading with entity ai data access
2026-07-14 12:22:17 +08:00
+ if (io.nanachiyo0721.shiroha.config.modules.fixes.PathfindingFixesConfig.doNotPathfindToNotOwnedTargets) {
2026-07-09 00:03:09 +08:00
+ // filter the targets not owned by current region
+ targets = new java.util.HashSet<>(targets); // well no idea about how to determine if this should be copied to a modifiable one
+ targets.removeIf(pos -> !ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.mob.level(), pos));
+
+ // return if no available (observe the logic in the first if block)
+ if (targets.isEmpty()) {
+ return null;
+ }
+ }
2026-07-14 11:32:51 +08:00
+ // Shiroha end
2026-07-09 00:03:09 +08:00
ProfilerFiller profiler = Profiler.get();
profiler.push("pathfind");
BlockPos fromPos = above ? this.mob.blockPosition().above() : this.mob.blockPosition();
2026-08-15 01:58:11 +08:00
@@ -286,6 +298,17 @@ public abstract class PathNavigation {
if (!this.isDone()) {
Vec3 target = this.path.getNextEntityPos(this.mob);
+ // Shiroha start - Fix region threading with entity ai data access
+ if (io.nanachiyo0721.shiroha.config.modules.fixes.PathfindingFixesConfig.breakDownPathfindingWhenOutOfRegion) {
+ // we assume that:
+ // 1. The code above doesn't touch the 'main thread context' with the position from 'this.path'
+ // 2. The pathfinder could correctly recompute or discard the incorrect target position and this situation is happening rarely
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.mob.level(), target)) {
+ this.hasDelayedRecomputation = true;
+ return;
+ }
+ }
+ // Shiroha end
this.mob.getMoveControl().setWantedPosition(target.x, this.getGroundY(target), target.z, this.speedModifier);
}
}
diff --git a/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java b/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java
index 2b5ba8f01b57b5e54bba4a8624f17e1b550d818e..bf127e91354b784dc8f7db910c46e3f7c42eec67 100644
--- a/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java
@@ -44,7 +44,7 @@ public class WallClimberNavigation extends GroundPathNavigation {
super.tick();
} else {
if (this.pathToPosition != null) {
- if (!this.pathToPosition.closerToCenterThan(this.mob.position(), this.mob.getBbWidth())
+ if ((io.nanachiyo0721.shiroha.config.modules.fixes.PathfindingFixesConfig.breakDownPathfindingWhenOutOfRegion && ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.mob.level(), this.pathToPosition)) && !this.pathToPosition.closerToCenterThan(this.mob.position(), this.mob.getBbWidth()) // Shiroha - Fix region threading with entity ai data access
&& (
!(this.mob.getY() > this.pathToPosition.getY())
|| !BlockPos.containing(this.pathToPosition.getX(), this.mob.getY(), this.pathToPosition.getZ())
2026-07-09 00:03:09 +08:00
diff --git a/net/minecraft/world/entity/animal/allay/AllayAi.java b/net/minecraft/world/entity/animal/allay/AllayAi.java
2026-08-15 01:58:11 +08:00
index c3667e7997551e0f5ce63bc6ee890082d1431400..edf2be5c1a48d80eaf900c782d02ea6f517fee2c 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/animal/allay/AllayAi.java
+++ b/net/minecraft/world/entity/animal/allay/AllayAi.java
@@ -112,6 +112,16 @@ public class AllayAi {
Optional<GlobalPos> likedNoteblockPos = brain.getMemory(MemoryModuleType.LIKED_NOTEBLOCK_POSITION);
if (likedNoteblockPos.isPresent()) {
GlobalPos position = likedNoteblockPos.get();
2026-07-14 11:32:51 +08:00
+ // Shiroha - Fix region threading with entity ai data access
2026-07-09 00:03:09 +08:00
+ final Level targetLevel = allay.level().getServer().getLevel(position.dimension());
+ final BlockPos targetPos = position.pos();
+
+ // thread checks
2026-08-15 01:58:11 +08:00
+ if (targetLevel == null || !ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(targetLevel, targetPos)) {
2026-07-09 00:03:09 +08:00
+ brain.eraseMemory(MemoryModuleType.LIKED_NOTEBLOCK_POSITION); // The memory value is not being belong to current tick region anymore
+ return Optional.empty();
+ }
2026-07-14 11:32:51 +08:00
+ // Shiroha
2026-07-09 00:03:09 +08:00
if (shouldDepositItemsAtLikedNoteblock(allay, brain, position)) {
return Optional.of(new BlockPosTracker(position.pos().above()));
}
diff --git a/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/net/minecraft/world/entity/animal/sniffer/Sniffer.java
2026-07-14 15:09:17 +08:00
index d5394ae7ce56555ad21aeafb2d78c291c62e2988..d172b819377c20ecd12331377fdd16f757051389 100644
2026-07-09 00:03:09 +08:00
--- a/net/minecraft/world/entity/animal/sniffer/Sniffer.java
+++ b/net/minecraft/world/entity/animal/sniffer/Sniffer.java
@@ -279,8 +279,18 @@ public class Sniffer extends Animal {
private boolean canDig(final BlockPos position) {
return this.level().getBlockState(position).is(BlockTags.SNIFFER_DIGGABLE_BLOCK)
- && this.getExploredPositions().noneMatch(explored -> GlobalPos.of(this.level().dimension(), position).equals(explored))
- && Optional.ofNullable(this.getNavigation().createPath(position, 1)).map(Path::canReach).orElse(false);
2026-07-14 11:32:51 +08:00
+ && this.getExploredPositions().noneMatch(explored -> { // Shiroha - Fix region threading with entity ai data access
2026-07-09 00:03:09 +08:00
+ // thread checks
+ final Level targetLevel = net.minecraft.server.MinecraftServer.getServer().getLevel(explored.dimension());
+ final BlockPos targetPos = explored.pos();
+
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(targetLevel, targetPos)) {
+ return false;
+ }
+
+ return GlobalPos.of(this.level().dimension(), position).equals(explored); // Original logic
2026-07-14 11:32:51 +08:00
+ }) // Shiroha end
2026-07-09 00:03:09 +08:00
+ && Optional.ofNullable(this.getNavigation().createPath(position, 1)).map(Path::canReach).orElse(false);
}
private void dropSeed() {